delphi变量值在线程中循环更改

时间:2019-06-09 15:57:33

标签: delphi

我的代码正在运行一个for循环来处理某些数据,例如此处

procedure printValue(Value: Integer);
begin
  TThread.Synchronize(TThread.Current, procedure
  begin
   form1.memo1.lines.add( Value.ToString );
  end);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  myThread : TThread;
  Proc1: TMyProc;
begin
  for I := 0 to 10 do
  begin
    myThread := TThread.CreateAnonymousThread(
    procedure
    begin
      printValue( i );
    end);
    myThread.Start;
  end;
end;

输出的代码如下:

3
5
6
8
9
11
10
4
11
4
7

这不是很好,所以我在线程启动后添加了一个小延迟,例如sleep(1)。这将解决输出问题,但不是一个好主意,因为在较大的循环块中,ui线程因此请尝试使用{{3} },因此我的代码更改如下:

function CaptureValue(Value: Integer): TMyProc;
begin
  Result := procedure begin Writeln(Value); end;
end;

procedure printValue(Value: Integer);
begin
  TThread.Synchronize(TThread.Current, procedure
  begin
   form1.memo1.lines.add( Value.ToString );
  end);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  myThread : TThread;
  Proc1: TMyProc;
begin
  for I := 0 to 10 do
  begin

    myThread := TThread.CreateAnonymousThread(
    procedure
    begin
      Proc1:= CaptureValue(i);
      printValue( Proc1 );
    end);
    myThread.Start;
  end;
end;

但是我遇到了[dcc32 Error] Unit11.pas(57): E2010 Incompatible types: 'Integer' and 'procedure, untyped pointer or untyped parameter'错误。

我的代码有什么问题?

1 个答案:

答案 0 :(得分:6)

匿名过程捕获变量,而不是值。 This is documented behavior。因此,在您的两个示例中,您的线程共享一个单独的I变量,并且对该变量的访问没有同步。

您有正确的想法将I传递给过程,然后在使用它之前从参数列表中捕获它。但是,您以错误的方式进行操作。

第二个示例实际无法编译的原因是因为您滥用CaptureValue()。它返回一个TMyProc,它显然是一个reference to procedure(顺便说一句,RTL为此目的已经有一个TProc)。您正在按原样将匿名过程传递给printValue(),而该过程将使用Integer。这就是编译器错误所抱怨的。

您使用返回值的方式CaptureValue()必须返回一个本身返回Integer的匿名函数(即让CaptureValue()返回reference to function: Integer ,也称为TFunc<Integer>),然后调用该函数并将 that 返回值传递给PrintValue()

但是,在将I传递给CaptureValue()之前,您仍在捕获自身,因此您仍然具有共享I的线程。您可能需要在调用CaptureValue()之前直接从循环内部调用CreateAnonymousThread()。但是,然后,您的线程将改为捕获并共享Proc1变量,因此您将回到原来的问题。

话虽如此,请尝试以下类似操作:

procedure PrintValue(Value: Integer);
begin
  TThread.Synchronize(nil,
    procedure
    begin
      Form1.Memo1.Lines.Add( Value.ToString );
    end
  );
end;

function CaptureAndPrintValue(Value: Integer): TProc;
begin
  Result := procedure
    begin
      printValue( Value );
    end
  );
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to 10 do
  begin
    TThread.CreateAnonymousThread(
      CaptureAndPrintValue(I)
    ).Start;
  end;
end;

或者,您可以让RTL为您处理线程:

uses
  ..., System.Threading;

procedure PrintValue(Value: Integer);
begin
  // can't use TThread.Synchronize() with TParallel, as it
  // doesn't service the main message queue while looping...
  TThread.Queue(nil,
    procedure
    begin
      Form1.Memo1.Lines.Add( Value.ToString );
    end
  );
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  TParallel.For(0, 10,
    procedure(I: Integer)
    begin
      PrintValue(I);
    end
  );
end;