delphi变量值在循环中的线程处更改 [英] delphi variable value is changing at thread in a loop

查看:46
本文介绍了delphi变量值在循环中的线程处更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

my code is running a for loop to process some data like here

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线程,因此请尝试使用此文档作为帮助,因此我的代码更改如下:

this is not good so i add a small delay like sleep(1) after thread start.this will fix output problem but not a good idea because in a large loop block the ui thread so try to use this document as help so my code changed like this:

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错误] Unit11.pas(57):E2010不兼容的类型:整数"和过程,无类型指针或无类型参数" 错误.

我的代码有什么问题?

推荐答案

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

Anonymous procedures capture variables, not values. This is documented behavior. So in both of your examples, your threads are sharing a single I variable with no synchronization over the access of that variable.

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

You had the right idea to pass I to a procedure and then capture it from the argument list before using it. However, you went about it the wrong way.

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

The reason your second example actually fails to compile is because you are misusing CaptureValue(). It returns a TMyProc, which is clearly a reference to procedure (which BTW, the RTL already has a TProc for that same purpose). You are passing an anonymous procedure as-is to printValue(), which takes an Integer instead. That is what the compiler error is complaining about.

您使用返回值的方式 CaptureValue()必须改为返回一个本身返回 Integer 的匿名函数(即,具有 CaptureValue()返回对函数的引用:整数,又名 TFunc< Integer> ),然后调用该函数并传递将值返回到 PrintValue().

The way you are using the return value, CaptureValue() would have to instead return an anonymous function that itself returns an Integer (ie, have CaptureValue() return a reference to function: Integer, aka TFunc<Integer>), then call that function and pass that return value to PrintValue().

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

You are still capturing I itself before passing it to CaptureValue(), though, so you still have threads sharing I. You would need to call CaptureValue() from inside the loop directly before calling CreateAnonymousThread(). But then, your threads would be capturing and sharing the Proc1 variable instead, so you would be right back to the original problem.

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

With that said, try something more like this instead:

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为您处理线程:

Or, you can let the RTL handle the threading for you:

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;

这篇关于delphi变量值在循环中的线程处更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆