【问题标题】:Pass parameters to Synchronize procedure call将参数传递给同步过程调用
【发布时间】:2014-01-28 15:32:51
【问题描述】:

创建线程对象时,我想从需要同步的应用程序中调用代码。问题是我不知道如何为带有参数的应用程序函数调用Synchronize

假设我们有

procedure ThreadObject.Execute;
var
  val1,val2:integer;
  Star:string;
begin
  Synchronize(funcyfunc); //how to pass val1,val2,star here?
end;

funcyfunc 定义如下

procedure OtherClass.funcyfunc(param1,param2:integer;spok:string);
begin
   letsCallFriends(spok,param1);
   letsCallFriends(spok,param2);
end;

现在解决这个问题的奇怪方法是在 ThreadObject private 中说

private
  star:string;
  val1,val2:integer;
  procedure starVal;

在实现中我们会这样做

procedure ThreadObject.starVal;
begin
  funcyfunc(Star,val1,val2);
end;

在线程执行中我们更改为

procedure ThreadObject.Execute;
var
  val1,val2:integer;
  Star:string;
begin
  Synchronize(starVal); //how to pass val1,val2,star here?
end;

但这很麻烦,因为每个过程调用都必须有一个全局变量。有没有更好的办法?

代码:

unit ThreadObject;

interface

uses
  Classes;

type
  TThreadObject= class(TThread)
  private
    star:string;
    val1,val2:integer;
    procedure starVal;
  protected
    procedure Execute; override;

    //assume we have a constructor that ini the star val1 val2.
  end;

implementation

{ TThreadObject }

procedure ThreadObject.Execute;
var
  val1,val2:integer;
  Star:string;
begin
  Synchronize(starVal); //how to pass val1,val2,star here?
end;

procedure ThreadObject.starVal;
begin
  funcyfunc(Star,val1,val2);
end;

end.

【问题讨论】:

标签: multithreading delphi delphi-2009 synchronize


【解决方案1】:

使用匿名方法

您可以在TThread.Synchronize 的手册中阅读

Synchronize(
  procedure
  begin
    Form1.Memo1.Lines.Add('Begin Execution');
  end);

在上面的示例中,引用的变量Form1 有点像被复制并保存,直到过程被执行(即称为变量捕获)。 p>

注意:大卫反对“复制变量”的想法,他可能是对的。然而,讨论所有极端情况和实现细节将是一种矫枉过正。他与“全局变量”OTOH 的比较可能会遇到递归过程等问题。所有易于掌握的类比都非常粗糙,并且以某种方式倾斜 ISTM。

您的匿名程序应该使用您的变量 val1val2,然后它们也应该被捕获

http://docwiki.embarcadero.com/Libraries/XE5/en/System.Classes.TThread.Synchronize

【讨论】:

  • FWIW,变量未复制。它被捕获。如果您有另一个捕获相同变量的 anon 方法,则两个 anon 方法都引用 same 变量。通过一种匿名方法对该 var 所做的更改在另一种匿名方法中可见。通过这种方式,捕获的变量从局部转换为全局。
  • @DavidHeffernan 如果本地过程本身改变了创建两个匿名方法之间的值?
  • 所有三方,声明局部变量的过程和两个匿名方法,都引用同一个变量。事实上,变量不再驻留在堆栈上,因为匿名方法的范围当然可以超过声明变量的过程的范围。捕获的变量被提升为驻留在堆上的对象。
  • 我无法找到“匿名方法”。被引入了,但是,从这里看来:docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/… 它是从 2009 年的 delphi 引入的。
  • 是的,D2009 确实引入了匿名方法。
【解决方案2】:

如果您使用的是不支持匿名方法的旧版 Delphi,您可以在类中创建“参数”字段。即

procedure ThreadObject.Execute;
var
  val1,val2:integer;
  Star:string;
begin
  FVal1 := val1;
  FVal2 := val2;
  FStar := Star;
  Synchronize(funcyfunc);
end;

procedure ThreadObject.FuncyFunc;
begin
  //Do stuff with FVal1, FVal2 and FStar
end;

请注意,这些不是全球性的。
这也是相当安全的,因为Synchronize 的目的是确保两个线程相互协调。因此,如果您没有 其他 线程尝试访问相同的数据,您将不会有任何竞争条件。

是的,这有点“笨拙”,但这是在较新版本的 Delphi 中使用匿名方法的优势。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-16
    • 2016-04-05
    • 1970-01-01
    相关资源
    最近更新 更多