【发布时间】:2021-07-13 16:54:19
【问题描述】:
我在一个名为 Execution 的单元中有一个数字运算应用程序,它是从主窗体调用的。通常 Execution.pas 中的代码需要连续运行 10-15 次,我正在寻找一种应用多线程的有效方法,以便执行单元上的计算并行运行。简化版代码如下:
其中有一个 Button1 的主窗体:
unit MainForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Execution;
type
TMainForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
var
MainForm1: TMainForm1;
implementation
{$R *.dfm}
procedure TMainForm1.Button1Click(Sender: TObject);
var
ExecutionThread: TThread;
run_ID: integer;
begin
for run_ID := 0 to 2 do
begin
ExecutionThread := TThread.CreateAnonymousThread(
procedure
begin
Execution.CalculateSum;
end);
ExecutionThread.FreeOnTerminate := true;
ExecutionThread.Start;
end;
end;
end.
Execution.pas 单位:
unit Execution;
interface
uses System.SysUtils, Vcl.Dialogs;
procedure CalculateSum;
procedure IncrementSum(var Sum: integer);
implementation
const
NoOfTimes = 100;
var
Sum: integer;
procedure CalculateSum;
var
i: integer;
begin
Sum := 0;
for i := 0 to Pred(NoofTimes) do
begin
IncrementSum(Sum);
end;
ShowMessage('Sum = ' + IntToStr(Sum));
end;
procedure IncrementSum(var Sum: integer);
begin
Inc(Sum);
Sleep(10);
end;
end.
如果我执行简化的代码,我会收到三个总和接近 300 的消息,但不是预期的 300(285、287、289),因为所有线程同时更改相同的全局变量 Sum 并且有时增量会重叠。
有没有一种简单的方法可以为每个线程分配不同的内存,以便我得到三条总和为 100 的消息?还是我必须自己硬编码,在每个线程中分配不同的执行变量?
请注意,我拥有的原始应用程序包含数千个变量和记录,因此我正在寻找一种快速的方法来执行此操作而无需显着更改代码(当然,如果有的话)。
提前谢谢你。
【问题讨论】:
-
是的,您有责任确保线程不会同时对共享资源进行操作。有很多方法可以做到这一点,为每个线程硬编码单独的全局变量是一种方法,尽管不是一种非常优雅的方法。理想情况下,您会将所需的操作封装到一个类中,该类管理并包含所需的状态,并且您可以创建任意数量的实例。
-
遗憾的是,如果您的应用程序写得像您的示例一样糟糕,那么就没有简单的方法了。我已经为一个大型的、主要是非 OO 遗留应用程序(100% 全局变量和数百个单元的意大利面条)完成了此操作,由此产生的
TThread后代需要封装 300 多个以前的全局变量和数组,并且需要重写大约 10,000 行以前的全局代码到新类中。花了几个月的时间。这就是为什么开发人员通常会对写得不好的代码有强烈的反应——因为它会弄得一团糟,让你别无选择,只能做大量的工作来清理。 -
因此,最简单的方法是为 Execution.pas 的所有计算创建一个类,对吗?我希望有一种更简单的方法来解决我的问题(老实说,希望不高)
-
您已将代码放置在单元中,但单元不是成为工作对象的类。从 TThread 创建一个类,将方法和变量放入该类,创建该类的实例,调用 execute。如果要传入参数,请覆盖 create。并且永远不要更新 GUI 或从线程内部调用 ShowMessage。如果您想要结果,请在终止线程之前阅读它,或者在 GUI 中添加终止时的回调,以便它可以显示消息或获取结果。
-
不要从后台线程调用
ShowMessage(或任何与 UI 交互的东西)!
标签: multithreading delphi