相关疑难解决方法(0)

一种破坏TThread对象的正确方法

这个问题似乎微不足道,但我希望你不要忽视它.
在销毁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对象.我问这个问题,因为我从我的同事的代码中学到了他们通常使用第一个代码示例来销毁这些对象,但他们从来没有用来检查等待的线程是否没有被挂起,如果线程我认为有点危险可能会在代码中的某处暂停.因此,我试图找到一种破坏这个类的对象的通用方法,这将使代码更清晰,更安全.我希望我没有让情况变得更糟 - 你怎么看?

提前感谢您的建议.

delphi destructor tthread

16
推荐指数
2
解决办法
1万
查看次数

Thread.FreeOnTerminate:= True,内存泄漏和ghost运行

几年前,我决定永远不要仅仅依赖于将线程的FreeOnTerminate属性设置为true以确保其被破坏,因为我在应用程序终止时发现并推理了两件事:

  1. 它会产生内存泄漏,并且
  2. 程序终止后,线程仍然在笔记本键盘下方的某处运行.

我熟悉了一个解决方法,并没有一直困扰我.直到今晚,再次有人(在这种情况下为@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)

delphi multithreading memory-leaks delphi-7

13
推荐指数
1
解决办法
3696
查看次数

如何处理阻塞调用期间需要释放锁定的情况?

我有一些" 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导致所有非工作线程都运行?够跑吗?

delphi multithreading delphi-xe2

9
推荐指数
2
解决办法
184
查看次数

管理动态线程数

首先,我仍然熟悉多线程,并且不太了解术语.我需要确保我做得对,因为这是一个敏感话题.

产品规格

我正在构建的是一个包含动态线程数的组件.这些线程中的每一个都被重用于执行许多请求.我可以在创建它时以及在执行它之前为线程提供所有必要的细节,以及提供事件处理程序.一旦它被执行,我几乎完成了一个请求,我提供了另一个请求.请求从另一个独立的后台线程被送入这些线程,后台线程不断处理请求队列.所以这个系统有两个列表:1)请求记录列表,2)线程指针列表.

我正在使用TThread该类的后代(至少这是我熟悉的线程方法).我通过同步创建线程时分配的事件触发器从线程获得反馈.线程正在后台加载和保存数据,当它们完成后,它们会自行重置以准备处理下一个请求.

问题

现在的麻烦决定如何(通过组件的属性处理不断变化的允许线程的数量的情况下,当开始ActiveThreads: TActiveThreadRangeTActiveThreadRange= 1..20).因此,一次可以创建1到20个线程.但是,当我们说,使用这个组件的应用程序将此属性从5更改为3.此时,已经创建了5个线程,并且我不想强行释放该线程,如果它正好忙.我需要等到它完成后才能释放它.另一方面,如果属性从3更改为5,那么我需要创建2个新线程.我需要知道在这种情况下"跟踪"这些线程的正确方法.

可能性

以下是我可以想到的"跟踪"这些线程的一些可能方法......

  • 保持TList包含每个创建的线程 - 易于管理
  • 创建一个TList包含每个创建的线程的包装器或后代 - 更易于管理,但需要更多工作
  • 保持array包含每个创建的线程 - 这会比一个更好TList吗?
  • 创建一个包含每个创建的线程的数组包装器

但回到我原来的问题 - 当ActiveThreads属性减少时如何处理现有繁忙的线程?创建它们没有问题,但释放它们变得令人困惑.我通常制作自己解放的线程,但这是我第一次制作一个可以重复使用的线程.我只需要知道在不中断任务的情况下销毁这些线程的正确方法.

更新

根据反馈,我已经获得并开始实现OmniThreadLibrary(以及长期需要的FastMM).我也改变了我的方法 - 我可以创建这些线程进程而无需管理它们,也没有其他线程来处理队列...

  • 1个生成新进程的主方法
    • function NewProcess(const Request: TProcessRequest): TProcessInfo;
    • TProcessRequest 是一个记录,其中包含要执行的操作的规范(文件名,选项等)
    • TProcessInfo 是一个传回一些状态信息的记录.
  • 在创建新流程时,为事件"完成"事件处理事件处理程序.当组件收到此消息时,它将检查队列.
    • 如果命令排队,它会将活动进程限制与当前进程计数进行比较
    • >如果超出限制,只需停止,下一个完成的过程将执行相同的检查
    • >如果在限制范围内,启动另一个新流程(确保之前的流程完成后)
    • 如果没有命令排队,那么就停止
  • 每个进程在完成任务后都可以自行死亡(没有保持活动的线程)
  • 我不必担心另一个计时器或线程不断循环
    • 相反,每个进程都会破坏其自身并在执行此操作之前检查新请求

另一个更新

我实际上已经恢复使用了一个TThread,因为OTL非常不舒服.我喜欢在自己的课堂上把东西包裹起来.

delphi queue multithreading list delphi-xe2

7
推荐指数
2
解决办法
2373
查看次数

在TThread.OnTerminate上设置/更改TThread.FreeOnTerminate

我一直在试图设置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)

delphi tthread

0
推荐指数
1
解决办法
1613
查看次数