同步方法,我可以用它来不是主线程吗?

aQu*_*Quu 2 delphi thread-synchronization

我很少使用线程,我对这个类有疑问:

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.
Run Code Online (Sandbox Code Playgroud)

如果任何模式与超时缓冲区匹配,则线程必须解密.但是其他线程(NOT MAIN)可以使用BuffUpdate方法更改缓冲区.我是否正确使用了同步方法?

Rem*_*eau 7

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

话虽这么说,如果主UI线程不需要触摸您的数据,那么Synchronize()这不是最好的解决方案.你可以只用包裹的同步对象的数据访问,而不是像一TCriticalSection,TMutex,TEvent,TMREWSync,Sytem.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.
Run Code Online (Sandbox Code Playgroud)