同步多个线程

时间:2014-03-21 02:07:46

标签: multithreading delphi synchronization delphi-xe5

我正在尝试在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个或更多线程执行相同的项目)。 现在我对线程和多线程相当新,我想知道这是否是正确的方法。

1 个答案:

答案 0 :(得分:4)

除了Ken所说的关于UI安全的内容之外,您正在获取处理相同项目的线程,因为您的mutex变量存在竞争条件。多个线程可能会同时看到mutex=false,因为它们实际上并未与每个线程同步。您需要使用真正的互斥锁。查看TMutex课程。或者只是将大部分代码包装在TThread.Synchronize()中,让VCL为您处理同步。但那就失败了使用线程的目的。

您正在使用完全错误的设计来满足您的要求。您需要将工作线程逻辑与UI逻辑分开。有几种不同的方法可以做到这一点。

  1. 将您的工作职位放入线程安全的队列,例如TThreadList<T>TThreadedQueue<T>。每个线程可以定期检查队列,并在有一个可用作业时拉出它。

    一个。一种变化是使用I / O完成端口作为队列。使用PostQueuedCompletionStatus()将作业发布到IOCP,并让每个线程使用GetQueuedCompletionResult()来接收作业。这允许操作系统为您完成所有排队和拉动,同时允许线程在没有可用作业时休眠。

  2. 将您的线程放入处于睡眠状态的池中。准备好新工作后,请检查池。如果线程可用,请将其从池中拉出,将作业传递给它,然后将其唤醒。否则将作业放在线程安全的队列中。作业完成后,让该线程检查队列。如果某个作业可用,请将其从队列中拉出,否则将该线程放入轮询中并将其重新置于休眠状态。

  3. 在任何情况下,当一个作业完成处理时,该线程可以使用TThread.Synchronize()TThread.Queue()或您选择的任何其他线程间通信向作业结果的主线程通知。然后主线程可以根据需要更新UI。

    线程不应该触摸UI来发现新的工作。