我正在尝试在Delphi XE5中创建一个多线程应用程序(最多可以说是100个线程), 所有线程都将使用/更改主窗体中的列表框, 这是我的代码: 主单位:
private
//list to hold the dynamic created threads
SendThreads : TObjectList;
public
mutex : boolean;
...
procedure TMainForm.FormCreate(Sender: TObject);
begin
...
mutex := false;
SendThreads := TObjectList.Create;
end;
...
//Button to create and start the threads
procedure TMainForm.btnWorkClick(Sender: TObject);
var
Thread : TSendThread;
i, nbr : integer;
begin
if SendThreads.Count < spnThreadCount.Value then
begin
for I := 1 to spnThreadCount.Value - SendThreads.Count do
begin
nbr := SendThreads.Add(TSendThread.Create(true));
Thread := TSendThread(SendThreads.Items[nbr]);
Thread.FreeOnTerminate := true;
Thread.Start;
end;
end;
end;
线程单位:
uses MainUnit;
procedure TSendThread.Execute;
begin
QueryList;
end;
//Basically, this procedure checks if the item in the listbox contains '- Done.' which means that this
//item has been done by another thread, if not, the current thread deal with this item.
procedure TSendThread.QueryList;
var i : integer;
S : String;
begin
i := 0;
while i < MainForm.lstURL.Count do
begin
while MainForm.mutex do;
MainForm.mutex := true;
if pos(' - Done.', MainForm.lstURL.Items[i]) = 0 then
begin
S := MainForm.lstURL.Items[i];
Delete(S, 1, pos('?txt=', S) + length('?txt=') - 1);
MainForm.Memo1.Lines.Add(MainForm.lstURL.Items[i]);
MainForm.lstURL.Items[i] := MainForm.lstURL.Items[i] + ' - Done.';
MainForm.mutex := false;
SendAd(URL, S);
end
else
begin
Inc(i);
MainForm.mutex := false;
end;
end;
end;
如果线程数小于4,则此方法有效,但如果更多,则获得冗余结果(2个或更多线程执行相同的项目)。 现在我对线程和多线程相当新,我想知道这是否是正确的方法。
答案 0 :(得分:4)
除了Ken所说的关于UI安全的内容之外,您正在获取处理相同项目的线程,因为您的mutex
变量存在竞争条件。多个线程可能会同时看到mutex=false
,因为它们实际上并未与每个线程同步。您需要使用真正的互斥锁。查看TMutex
课程。或者只是将大部分代码包装在TThread.Synchronize()
中,让VCL为您处理同步。但那就失败了使用线程的目的。
您正在使用完全错误的设计来满足您的要求。您需要将工作线程逻辑与UI逻辑分开。有几种不同的方法可以做到这一点。
将您的工作职位放入线程安全的队列,例如TThreadList<T>
或TThreadedQueue<T>
。每个线程可以定期检查队列,并在有一个可用作业时拉出它。
一个。一种变化是使用I / O完成端口作为队列。使用PostQueuedCompletionStatus()
将作业发布到IOCP,并让每个线程使用GetQueuedCompletionResult()
来接收作业。这允许操作系统为您完成所有排队和拉动,同时允许线程在没有可用作业时休眠。
将您的线程放入处于睡眠状态的池中。准备好新工作后,请检查池。如果线程可用,请将其从池中拉出,将作业传递给它,然后将其唤醒。否则将作业放在线程安全的队列中。作业完成后,让该线程检查队列。如果某个作业可用,请将其从队列中拉出,否则将该线程放入轮询中并将其重新置于休眠状态。
在任何情况下,当一个作业完成处理时,该线程可以使用TThread.Synchronize()
,TThread.Queue()
或您选择的任何其他线程间通信向作业结果的主线程通知。然后主线程可以根据需要更新UI。
线程不应该触摸UI来发现新的工作。