这个问题似乎微不足道,但我希望你不要忽视它.
在销毁TThread对象之前,通常需要等到调用TThread.Execute()方法的线程完成,因为只有这样我们才能确定,例如,不再访问在类的析构函数中销毁的对象.因此,有必要调用Terminate来设置线程必须检查的Terminated标志以知道是否退出,然后调用WaitFor()方法.
因为线程可能被挂起,我认为在调用WaitFor之前恢复它是好的,否则调用线程将会死锁.并且因为线程可以多次挂起,所以应该恢复相同的次数,对吗?
while Suspended do
Resume;
Run Code Online (Sandbox Code Playgroud)
如果线程被创建为暂停,我们不必担心当我们恢复线程只会终止它时将调用TThread.Execute()方法 - 它不会(如果我错了请纠正我).
我所说的建议为每个被释放的TThread对象使用以下代码行:
MyThread.Terminate;
while MyThread.Suspended do
MyThread.Resume;
MyThread.WaitFor;
MyThread.Free;
Run Code Online (Sandbox Code Playgroud)
不幸的是,当我们销毁创建多个线程的应用程序时,为每个被破坏的TThread对象编写这样一段代码会使代码变得非常长,甚至可能不透明.
因此我得出结论,所有这些都可以放在TThread类的重写析构函数中,这要归功于调用MyThread.Free(如果设置了MyThread.FreeOnTerminate,则调用MyThread.Terminate),而不关心是否销毁object是否是TThread对象:
destructor TMyThread.Destroy;
begin
//if FreeOnTerminate, the calling thread cannot wait for itself
if GetCurrentThreadId <> ThreadId then
begin
Terminate;
while Suspended do
Resume;
WaitFor;
end;
{free all objects created in this class}
inherited Destroy;
end;
Run Code Online (Sandbox Code Playgroud)
请原谅我提出这样一个基本问题.但是,我希望通过这种方式了解您的意见 - 我希望这是一种普遍的方式 - 销毁TThread对象.我问这个问题,因为我从我的同事的代码中学到了他们通常使用第一个代码示例来销毁这些对象,但他们从来没有用来检查等待的线程是否没有被挂起,如果线程我认为有点危险可能会在代码中的某处暂停.因此,我试图找到一种破坏这个类的对象的通用方法,这将使代码更清晰,更安全.我希望我没有让情况变得更糟 - 你怎么看?
提前感谢您的建议.
几年前,我决定永远不要仅仅依赖于将线程的FreeOnTerminate属性设置为true以确保其被破坏,因为我在应用程序终止时发现并推理了两件事:
我熟悉了一个解决方法,并没有一直困扰我.直到今晚,再次有人(在这种情况下为@MartinJames)评论我的回答,其中我引用了一些不FreeOnTerminate与线程提前终止结合使用的代码.我回到RTL代码中,意识到我可能做出了错误的假设.但我也不太确定,因此这个问题.
首先,为了重现上述陈述,使用了这个说明性代码:
unit Unit3;
interface
uses
Classes, Windows, Messages, Forms;
type
TMyThread = class(TThread)
FForm: TForm;
procedure Progress;
procedure Execute; override;
end;
TMainForm = class(TForm)
procedure FormClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FThread: TMyThread;
end;
implementation
{$R *.dfm}
{ TMyThread }
procedure TMyThread.Execute;
begin
while not Terminated do
begin
Synchronize(Progress);
Sleep(2000);
end;
end;
procedure TMyThread.Progress;
begin
FForm.Caption := FForm.Caption + '.';
end;
{ TMainForm }
procedure TMainForm.FormClick(Sender: TObject);
begin
FThread := …Run Code Online (Sandbox Code Playgroud) 我有一些" FreeOnTerminate"工作线程,它们在TThreadList开始执行时添加其句柄,并在执行结束时从中删除.他们还会检查一个全局事件对象,通知他们取消他们的工作.
以下是在主线程中运行的部件,它向事件发出信号并等待可能的工作线程结束.WorkerHandleList是全球性的ThreadList.
...
procedure WaitForWorkers;
var
ThreadHandleList: TList;
begin
ThreadHandleList := TWorkerThread.WorkerHandleList.LockList;
TWorkerThread.WorkerHandleList.UnlockList;
WaitForMultipleObjects(ThreadHandleList.Count,
PWOHandleArray(ThreadHandleList.List), True, INFINITE);
end;
initialization
TWorkerThread.RecallAllWorkers := TEvent.Create;
TWorkerThread.WorkerHandleList := TThreadList.Create;
finalization
TWorkerThread.RecallAllWorkers.SetEvent;
WaitForWorkers;
TWorkerThread.RecallAllWorkers.Free;
TWorkerThread.WorkerHandleList.Free;
Run Code Online (Sandbox Code Playgroud)
我认为,这种设计有一个缺陷,即我必须在等待线程的句柄之前解锁列表,因为这会导致死锁,因为线程本身会从同一列表中删除它们的句柄.没有任何锁定,上下文切换可能导致线程释放自己导致WaitForMultipleObjects立即返回WAIT_FAILED.我不能使用另一个锁,因为WaitForMultipleObjects阻塞,我将无法从主线程释放锁.
我可以通过多种方式修改此设计,包括不使用FreeOnTerminate线程,这将保证有效句柄,直到它们被明确释放.或者仅从主线程修改线程句柄列表.或者其他人......
但我想问的是,如果不改变设计,是否有解决这类问题的方法?例如,在从列表中删除其句柄之前,是否会在工作线程代码中睡眠,或者调用SwitchToThread导致所有非工作线程都运行?够跑吗?
首先,我仍然熟悉多线程,并且不太了解术语.我需要确保我做得对,因为这是一个敏感话题.
产品规格
我正在构建的是一个包含动态线程数的组件.这些线程中的每一个都被重用于执行许多请求.我可以在创建它时以及在执行它之前为线程提供所有必要的细节,以及提供事件处理程序.一旦它被执行,我几乎完成了一个请求,我提供了另一个请求.请求从另一个独立的后台线程被送入这些线程,后台线程不断处理请求队列.所以这个系统有两个列表:1)请求记录列表,2)线程指针列表.
我正在使用TThread该类的后代(至少这是我熟悉的线程方法).我通过同步创建线程时分配的事件触发器从线程获得反馈.线程正在后台加载和保存数据,当它们完成后,它们会自行重置以准备处理下一个请求.
问题
现在的麻烦决定如何(通过组件的属性处理不断变化的允许线程的数量的情况下,当开始ActiveThreads: TActiveThreadRange该TActiveThreadRange= 1..20).因此,一次可以创建1到20个线程.但是,当我们说,使用这个组件的应用程序将此属性从5更改为3.此时,已经创建了5个线程,并且我不想强行释放该线程,如果它正好忙.我需要等到它完成后才能释放它.另一方面,如果属性从3更改为5,那么我需要创建2个新线程.我需要知道在这种情况下"跟踪"这些线程的正确方法.
可能性
以下是我可以想到的"跟踪"这些线程的一些可能方法......
TList包含每个创建的线程 - 易于管理TList包含每个创建的线程的包装器或后代 - 更易于管理,但需要更多工作array包含每个创建的线程 - 这会比一个更好TList吗?但回到我原来的问题 - 当ActiveThreads属性减少时如何处理现有繁忙的线程?创建它们没有问题,但释放它们变得令人困惑.我通常制作自己解放的线程,但这是我第一次制作一个可以重复使用的线程.我只需要知道在不中断任务的情况下销毁这些线程的正确方法.
更新
根据反馈,我已经获得并开始实现OmniThreadLibrary(以及长期需要的FastMM).我也改变了我的方法 - 我可以创建这些线程进程而无需管理它们,也没有其他线程来处理队列...
function NewProcess(const Request: TProcessRequest): TProcessInfo;TProcessRequest 是一个记录,其中包含要执行的操作的规范(文件名,选项等)TProcessInfo 是一个传回一些状态信息的记录.另一个更新
我实际上已经恢复使用了一个TThread,因为OTL非常不舒服.我喜欢在自己的课堂上把东西包裹起来.
我一直在试图设置FreeOnTerminate属性中的OnTerminate过程,但它似乎像它要么为时已晚来设置它,或者它完全无视write程序.
如何设置/更改FreeOnTerminate属性中的OnTerminate程序?那有什么变通方法吗?
一点点代码:
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm2 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
procedure OnTestThreadTerminate (Sender : TObject);
public
{ Public declarations }
end;
type
TTestThread = class (TThread)
public
procedure Execute; override;
end;
var
Form2: TForm2;
GlobalThreadTest : TTestThread;
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin …Run Code Online (Sandbox Code Playgroud)