我的代码正在运行一个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'
错误。
我的代码有什么问题?
答案 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;