delphi - 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 线程,所以尝试使用这个文档作为帮助,所以我的代码更改如下:
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'
错误。
我的代码有什么问题?
解决方案
匿名过程捕获变量,而不是值。这是记录在案的行为。因此,在您的两个示例中,您的线程都共享一个I
变量,而对该变量的访问没有同步。
您有正确的想法传递I
给一个过程,然后在使用它之前从参数列表中捕获它。然而,你走错了路。
您的第二个示例实际上无法编译的原因是您滥用CaptureValue()
. 它返回 a TMyProc
,这显然是 a reference to procedure
(顺便说一句,RTL 已经有 aTProc
用于相同目的)。您正在将一个匿名过程按原样传递给printValue()
,而它需要一个Integer
。这就是编译器错误所抱怨的。
您使用返回值的方式CaptureValue()
必须改为返回一个匿名函数,该函数本身返回一个Integer
(即,CaptureValue()
返回一个reference to function: Integer
,又名TFunc<Integer>
),然后调用该函数并将该返回值传递给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;
推荐阅读
- amazon-web-services - AWS - 通过 NAT 网关发送 Internet 流量和私有到另一个 VPC
- angular - Angular 服务从回调函数返回 observable
- reactjs - 如何在单个二进制文件中使用 ReactJS GUI 构建 Go 应用程序?
- ssis - 跨域访问共享文件夹时,SSIS 包无法使用 SQL 作业执行
- excel - 如何引用旧版本的 VBA 库?
- java - 骆驼:ClassCastException:DefaultMessage 无法转换为类 SnmpMessage
- angular - 使用 Firebase 将子集合查询到文档中
- python - 如何将数据从字典检索到表?
- javascript - 这个结构有名字吗?它是否一直被支持?
- php - sFetch 以指定字段为实体的数据