同步方法,我可以将它用于非主线程吗?

Synchronize Method, can I use it to not main thread?

我很少使用线程,对此我有疑问class:

unit ExpectingThread;

interface

uses
    System.Classes;

type
    TExpectingThread = class(TThread)
    private
        _timeoutMs: Integer;
        _buff: string;
        _patterns: TArray<string>;
        _result: Integer;
        function Timeouted(startTime: Cardinal): Boolean;
        function ExpectedDetected: Boolean;

    protected
        procedure Execute; override;
    public
        constructor Create(patterns: TArray<string>; buff: string; timeoutMs: Integer);
        //this method is called from other NOT MAIN thread
        procedure BuffUpdate(text: string);
    end;

implementation

uses
    Winapi.Windows,
    System.RegularExpressions;

{ TExpectingThread }

constructor TExpectingThread.Create(patterns: TArray<string>; buff: string; timeoutMs: Integer);
begin
    _patterns := patterns;
    _timeoutMs := timeoutMs;
    _buff := buff;
end;

//this method is called from other NOT MAIN thread
procedure TExpectingThread.BuffUpdate(text: string);
begin
    // lock
    TThread.Synchronize(Self, procedure
        begin
            _buff := _buff + text;
        end);
    // unlock
end;

procedure TExpectingThread.Execute;
var
    startTime: Cardinal;
begin
    inherited;

    startTime := GetTickCount;
    while true do
    begin
        if Timeouted(startTime) then
        begin
            Self.ReturnValue := 0; // timeouted
            Exit;
        end;

        if ExpectedDetected then
        begin
            Self.ReturnValue := 1; // found
            Exit;
        end;
    end;
end;

function TExpectingThread.ExpectedDetected: Boolean;
var
    regex: TRegEx;
    i: Integer;
begin
    // lock
    result := 0;
    for i := 0 to High(_patterns) do
    begin
        regex.Create(_patterns[i]);
        if regex.IsMatch(_buff) then
        begin
            _result := i;
            Exit(true);
        end;
    end;
    // unlock
end;

function TExpectingThread.Timeouted(startTime: Cardinal): Boolean;
var
    currentTime: Cardinal;
begin
    currentTime := GetTickCount;
    result := currentTime - startTime > _timeoutMs;
end;

end.

线程必须检查是否有任何模式与缓冲超时匹配。但是其他线程(非主线程)可以使用 BuffUpdate 方法更改缓冲区。我是否正确使用了同步方法?

Synchronize() 专门设计用于与主 UI 线程一起工作。您 可以 将它用于线程间同步,但是所有涉及的线程都必须使用它。在您的示例中,只有写入 _buff 的线程正在使用它,但从 _buff 读取的线程不是。所以这是你逻辑上的一个漏洞。

也就是说,如果 UI 主线程不需要接触您的数据,那么 Synchronize() 不是最好的解决方案。您可以只用同步对象包装数据访问,例如 TCriticalSectionTMutexTEventTMREWSyncSytem.TMonitor 等。例如:

unit ExpectingThread;

interface

uses
  System.Classes, System.SyncObjs;

type
  TExpectingThread = class(TThread)
    private
      _timeoutMs: Integer;
      _buff: string;
      _buffLock: TCriticalSection;
      _buffChanged: Boolean;
      _patterns: TArray<string>;
      _result: Integer;
      function Timeouted(startTime: Cardinal): Boolean;
      function ExpectedDetected: Boolean;
    protected
      procedure Execute; override;
    public
      constructor Create(patterns: TArray<string>; buff: string; timeoutMs: Integer);
      destructor Destroy; override;
      //this method is called from other NOT MAIN thread
      procedure BuffUpdate(text: string);
    end;

implementation

uses
  Winapi.Windows, System.RegularExpressions;

{ TExpectingThread }

constructor TExpectingThread.Create(patterns: TArray<string>; buff: string; timeoutMs: Integer);
begin
  inherited Create(False);
  _buffLock := TCriticalSection.Create;
  _patterns := patterns;
  _timeoutMs := timeoutMs;
  _buff := buff;
  _buffChanged := True;
end;

destructor TExpectingThread.Destroy;
begin
  _buffLock.Free;
  inherited;
end;

//this method is called from other NOT MAIN thread
procedure TExpectingThread.BuffUpdate(text: string);
begin
  _buffLock.Enter;
  try
    _buff := _buff + text;
    _buffChanged := True;
  finally
    _buffLock.Leave;
  end;
end;

procedure TExpectingThread.Execute;
var
  startTime: DWORD;
begin
  startTime := GetTickCount;
  while not Terminated do
  begin
    if Timeouted(startTime) then
    begin
      Self.ReturnValue := 0; // timeouted
      Exit;
    end;
    if ExpectedDetected then
    begin
      Self.ReturnValue := 1; // found
      Exit;
    end;
  end;
end;

function TExpectingThread.ExpectedDetected: Boolean;
var
  i: Integer;
  buff: string;
begin
  Result := False;
  _buffLock.Enter;
  try
    If not _buffChanged then Exit;
    buff := _buff;
    UniqueStr(buff);
    _buffChanged := False;
  finally
    _buffLock.Leave;
  end;
  for i := Low(_patterns) to High(_patterns) do
  begin
    if TRegEx.IsMatch(buff, _patterns[i]) then
    begin
      _result := i;
      Exit(True);
    end;
  end;
end;

function TExpectingThread.Timeouted(startTime: Cardinal): Boolean;
var
  currentTime: DWORD;
begin
  currentTime := GetTickCount;
  result := currentTime - startTime > _timeoutMs;
end;

end.