Red*_*ber 1 delphi multithreading delphi-xe2
我有一个来自TThread的孩子.一切正常,但我怎么能大规模暂停或恢复我创建的线程?或者我怎么才能暂停第二个线程(在Button2Click中创建)?这是我的代码的一部分:
TMyThread = class(TThread)
private
source_file, destination_file: string;
total_size, current_size, download_item_id: integer;
protected
procedure ShowResult;
procedure Execute; override;
public
end;
var
MyThread: TMyThread;
begin
procedure TMyThread.Execute;
begin
//Some code for download file here, it doesn't matter
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
MyThread := TMyThread.Create(True);
MyThread.source_file :='http://example.com/download1.zip';
MyThread.destination_file := 'c:\download1.zip';
MyThread.download_item_id := 0;
MyThread.Priority := tpNormal;
MyThread.FreeOnTerminate := True;
MyThread.Resume;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
MyThread := TMyThread.Create(True);
MyThread.source_file :='http://example.com/download2.zip';
MyThread.destination_file := 'c:\download2.zip';
MyThread.download_item_id := 1;
MyThread.Priority := tpNormal;
MyThread.FreeOnTerminate := True;
MyThread.Resume;
end;
end.
Run Code Online (Sandbox Code Playgroud)
也就是说,如果我创建这样的线程 - 它对我有用:
var
MyThread1, MyThread2: TMyThread;
...
procedure TForm1.Button1Click(Sender: TObject);
begin
MyThread1 := TMyThread.Create(True);
MyThread1.source_file :='http://example.com/download1.zip';
MyThread1.destination_file := 'c:\download1.zip';
MyThread1.download_item_id := 0;
MyThread1.Priority := tpNormal;
MyThread1.FreeOnTerminate := True;
MyThread1.Resume;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
MyThread2 := TMyThread.Create(True);
MyThread2.source_file :='http://example.com/download2.zip';
MyThread2.destination_file := 'c:\download2.zip';
MyThread2.download_item_id := 1;
MyThread2.Priority := tpNormal;
MyThread2.FreeOnTerminate := True;
MyThread2.Resume;
end;
//Terminate all of TMyThread
procedure TForm1.Button3Click(Sender: TObject);
begin
MyThread1.Terminate;
MyThread2.Terminate;
ShowMessage('All downloads were terminated!');
end;
//Terminate ONLY the second of TMyThread
procedure TForm1.Button4Click(Sender: TObject);
begin
MyThread2.Terminate;
ShowMessage('The second download was terminated!');
end;
Run Code Online (Sandbox Code Playgroud)
但是如何为一组动态创建的TMyThread(如第一个代码示例中)执行此操作?
你真的,真的不应该保留对终止时设置为空闲的线程的引用.它要求各种各样的问题.下面的代码确实使用FreeOnTerminate设置为True的线程.这只是安全的,因为:1)引用在线程终止时和释放之前被删除; 2)在mainthread的上下文中调用OnTerminate处理程序和/或使用线程安全的TList后代; - 最重要的是 - 3)引用仅用于从主线程的上下文中有选择地取消线程.
一旦你想将引用用于其他任何东西(比如暂停和恢复)或者可以从主线程以外的任何线程的上下文中访问它们,你真的应该深入研究线程程序执行的各种机制.
几个关键字:线程同步,事件信号,互斥锁,信号量,关键部分.
使用Delphi进行多线程编程的一个很好的参考是多线程 - Delphi方式
如果您确实需要稍后引用线程,则需要将引用存储在某处.但是您不需要为您创建的每个线程都有单独的变量.有很多选择.我想到了一个数组,但最简单的可能是一个对象列表(来自Contnrs单元):
TForm1 = class(TForm)
private
MyThreads: TObjectList;
constructor TForm1.Create(AOwner: TComponent);
begin
inherited;
MyThreads := TObjectList.Create;
end;
destructor TForm1.Destroy;
begin
MyThreads.Free;
inherited;
end;
Run Code Online (Sandbox Code Playgroud)
TObjectList有一个OwnsObjects默认属性True.这意味着释放对象列表也将释放它包含的实例.这意味着您需要确保列表仅包含有效的引用.通常这不是问题,但是对于线程,您需要特别小心,特别是当您使用FreeOnTerminateset时True.
像在第一个示例中一样创建线程,并进行一些更改:
使用局部变量(您应该避免使用全局变量,即使它们仅在单元内尽可能可见).
将线程添加到对象列表中.
当您还想将'FreeOnTerminate`设置为True时,设置OnTerminate处理程序以从对象列表中删除实例.
procedure TForm1.Button1Click(Sender: TObject);
var
Thread: TThread; // Local var
begin
Thread := TMyThread.Create(True);
MyThreads.Add(Thread); // Add to list
Thread.source_file :='http://example.com/download1.zip';
Thread.destination_file := 'c:\download1.zip';
Thread.download_item_id := 0;
Thread.Priority := tpNormal;
Thread.OnTerminate := HandleThreadTerminate; // Ensure you keep list valid
Thread.FreeOnTerminate := True; // Advice against this!
Thread.Start;
end;
procedure TForm1.HandleThreadTerminate(Sender: TObject);
var
idx: Integer;
begin
// Acquire Lock to protect list access from two threads
idx := MyThreads.IndexOf(Sender);
if idx > -1 then
MyThreads.Delete(idx);
// Release lock
end;
Run Code Online (Sandbox Code Playgroud)
HandleThreadTerminate过程应该使用某种锁,因此两个线程不能同时尝试从列表中删除实例,因为IndexOf和Delete之间的线程切换可能意味着删除了错误的实例.处理程序中的代码可以写成MyThreads.Remove(Sender).虽然这是单个语句,但它不是线程安全的,因为它与我在幕后显示的代码相同.
编辑:实际上,正如@LURD提到的那样,它恰好是线程安全的,因为OnTerminate是在主线程的上下文中调用的.我将离开TThreadList示例,因为如果您需要/碰巧从多个线程访问列表,这是一个很好的方法.
要自动处理锁定内容,可以使用TThreadList(来自classes设备).所需的代码更改:
当然,您需要实例化TThreadList而不是TObjectList.
constructor TForm1.Create(AOwner: TComponent);
begin
inherited;
MyThreads := TThreadList.Create;
end;
Run Code Online (Sandbox Code Playgroud)
因为TThreadList没有概念OwnsObjects,你必须自己释放任何剩余的线程:
destructor TForm1.Destroy;
var
LockedList: TList;
idx: integer;
begin
LockedList := MyThreads.LockList;
try
for idx := LockedList.Count - 1 downto 0 do
TObject(MyThreads.Items(idx)).Free;
finally
MyThreads.UnlockList;
end;
MyThreads.Free;
inherited;
end;
Run Code Online (Sandbox Code Playgroud)
现在可以安全地简化OnTerminate处理程序,因为TThreadList将处理所需的锁定.
procedure TForm1.HandleThreadTerminate(Sender: TObject);
begin
MyThreads.Remove(idx);
end;
Run Code Online (Sandbox Code Playgroud)
编辑:正如@mghie提到的那样,当FreeOnTerminate为真时释放一个线程与终止免费的整个想法发生冲突.您可以在释放线程(将终止它)之前将FreeOnTerminate设置为false,或者如果要保留FreeOnTerminate处理,则应断开OnTerminate处理程序,然后使用Terminate告诉线程终止.
destructor TForm1.Destroy;
var
LockedList: TList;
idx: integer;
begin
LockedList := MyThreads.LockList;
try
for idx := LockedList.Count - 1 downto 0 do
begin
TThread(MyThreads.Items(idx)).OnTerminate := nil;
TThread(MyThreads.Items(idx)).Terminate;
end;
finally
MyThreads.UnlockList;
end;
MyThreads.Free;
inherited;
end;
Run Code Online (Sandbox Code Playgroud)