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

delphi变量值在循环中的线程处更改,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;

我的代码运行一个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
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;
这不好,因此我在线程启动后添加了一个小延迟,如
sleep(1)
。这将解决输出问题,但不是一个好主意,因为在一个大循环中会阻塞ui线程,所以请尝试使用,因此我的代码更改如下:

3
5
6
8
9
11
10
4
11
4
7
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不兼容的类型:“Integer”和“procedure,untyped pointer或untyped parameter”
错误


我的代码怎么了?

匿名过程捕获变量,而不是值。因此,在这两个示例中,线程共享一个
I
变量,而对该变量的访问没有同步

您的想法是正确的,将
I
传递给一个过程,然后在使用它之前从参数列表中捕获它。然而,你走错了路

第二个示例实际无法编译的原因是您误用了
CaptureValue()
。它返回一个
TMyProc
,这显然是对过程的
引用(顺便说一句,RTL已经有一个
TProc
,用于相同的目的)。您正在将一个匿名过程按原样传递给
printValue()
,该过程取而代之的是
整数。这就是编译器错误所抱怨的

使用返回值的方式,
CaptureValue()
必须返回一个匿名函数,该函数本身返回一个
Integer
(即have
CaptureValue()
返回对函数的
引用:Integer
,也称为
TFunc
),然后调用该函数并将返回值传递给
PrintValue()

不过,在将
I
本身传递给
CaptureValue()
之前,您仍在捕获它,因此仍有线程共享
I
。在调用
CreateAnonymousThread()
之前,您需要直接从循环内部调用
CaptureValue()
。但是,您的线程将捕获并共享
Proc1
变量,因此您将返回到原始问题

话虽如此,请尝试类似的方法:

程序打印值(值:整数);
开始
TThread.Synchronize(nil,
程序
开始
Form1.Memo1.Lines.Add(Value.ToString);
结束
);
结束;
函数CaptureAndPrintValue(值:整数):TProc;
开始
结果:=程序
开始
打印值(Value);
结束
);
结束;
程序TForm1.按钮1单击(发送方:TObject);
变量
I:整数;
开始
对于I:=0到10 do
开始
TThread.CreateAnonymousThread(
CaptureAndPrintValue(一)
).开始;
结束;
结束;
或者,您可以让RTL为您处理线程:

使用
系统线程;
程序打印值(值:整数);
开始
//无法将TThread.Synchronize()与TParallel一起使用,因为
//循环时不为主消息队列提供服务。。。
TThread.Queue(nil,
程序
开始
Form1.Memo1.Lines.Add(Value.ToString);
结束
);
结束;
程序TForm1.按钮1单击(发送方:TObject);
开始
t(0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,,
过程(I:整数)
开始
印刷价值(I);
结束
);
结束;

错误信息中解释了错误所在,您的PrintValue需要一个整数,但您正在向过程传递一个引用。无论如何,我认为你的问题与变量捕获无关。您根本无法告诉线程何时开始/完成。。。。即使睡眠也不能保证工作,尽管它会增加获得预期结果的可能性。@SertacAkyuz是的,因此我必须避免睡眠。谢谢,但第一个代码输出是:
0 2 3 6 7 10 1 8 4 5 9
第二个代码被挂起,不显示任何输出@佩曼夫。考虑到循环的线程性质,该输出非常好。不能保证输出数字的顺序。如果您希望数字按顺序输出,那么您不了解线程是如何工作的。但您可以看到,与原始代码不同,不再重复任何数字。至于挂起,我忘记了
TThread.Synchronize()
不能与
TParallel.for()
一起工作。我更新了该示例,使用
TThread.Queue()
代替。是的,如果此时数据没有丢失,对我来说就足够了。但是为什么线程启动得越早,将值传递到memo@佩曼夫。这就是线程的工作方式。它们并行运行。仅仅因为您按顺序启动它们并不意味着它们最终将以相同的顺序同步回UI。这取决于系统定时。创建线程与实际开始运行线程之间的时间间隔。在同一CPU上运行的线程之间切换任务的计时。在不同CPU上运行的线程之间的计时。
在同一CPU上运行的线程之间切换任务的计时。在不同CPU上运行的线程之间的计时
非常容易理解。感谢您提供的有用信息。。