delphi中的多线程队列?

时间:2013-04-25 02:35:29

标签: multithreading delphi queue

这是我关于此问题的第二个问题,我对此有一些麻烦>。<

好吧,我只是想创建有限数量的线程(在这种情况下,我想要10个线程),然后每个线程都会在我的列表中选择一个名称并在我的站点中获取一些数据。

我的系统运行良好,但我的多线程系统仍然失败=(

-

我尝试了LU RD发布的代码,但主线程没有等待线程完成队列,只是停止=(

代码:

uses
Classes,SyncObjs,Generics.Collections;

Type
TMyConsumerItem = class(TThread)
private
 FQueue : TThreadedQueue<TProc>;
 FSignal : TCountDownEvent;
protected
 procedure Execute; override;
public
 constructor Create( aQueue : TThreadedQueue<TProc>; aSignal : TCountdownEvent);
end;

constructor TMyConsumerItem.Create(aQueue: TThreadedQueue<TProc>; aSignal : TCountDownEvent);
begin
 Inherited Create(false);
 Self.FreeOnTerminate := true;
 FQueue := aQueue;
 FSignal := aSignal;
end;

procedure TMyConsumerItem.Execute;
var
aProc : TProc;
begin
 try
 repeat
  FQueue.PopItem(aProc);
  if not Assigned(aProc) then
   break; // Drop this thread
  aProc();
 until Terminated;
 finally
  FSignal.Signal;
 end;
end;

procedure DoSomeJob(myListItems : TStringList);
const
 cThreadCount = 10;
 cMyQueueDepth = 100;
var
i : Integer;
aQueue : TThreadedQueue<TProc>;
aCounter : TCountDownEvent;
function CaptureJob( const aString : string) : TProc;
begin
 Result :=
  procedure
  begin
    // Do some job with aString
  end;
end;
begin
aQueue := TThreadedQueue<TProc>.Create(cMyQueueDepth);
aCounter := TCountDownEvent.Create(cThreadCount);
try
 for i := 1 to cThreadCount do
  TMyConsumerItem.Create(aQueue,aCounter);
 for i := 0 to myListItems.Count-1 do begin
  aQueue.PushItem( CaptureJob( myListItems[i]));
 end;
finally
 for i := 1 to cThreadCount do
  aQueue.PushItem(nil);
 aCounter.WaitFor;  // Wait for threads to finish
 aCounter.Free;
 aQueue.Free;
end;
end;

我的另一个问题:Multi Thread Delphi

我正在使用Delphi XE3。

1 个答案:

答案 0 :(得分:5)

  • 首先,如果你想调用程序DoSomeJob()并阻塞直到主线程准备就绪,那就有一个警告。如果您的工作线程正在与主线程同步,则aCounter.WaitForTThread.Synchronize()会出现死锁情况。

我假设这就是你正在发生的事情,在这里猜测。

有一种方法可以解决这个问题,我将在这个答案中展示。

  • 其次,通常工作线程应由线程池处理,以避免始终创建/销毁线程。将您的工作传递给线程池,因此所有内容都在线程内运行并等待。这可以避免阻塞主线程。 我会把这个留给你。编写该框架后,线程将更容易。如果这看起来很复杂,请尝试使用OTL线程框架。

以下是主线程可以安全地等待DoSomeJob()的示例。 创建一个匿名线程以等待aCounter发出信号。 此示例使用TMemoTButton。只需使用这些组件创建一个表单,然后将按钮OnClick事件连接到ButtonClick方法。

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure DoSomeJob( myListItems : TStringList);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  SyncObjs, Generics.Collections;

{- Include TMyConsumerItem class here }

procedure TForm1.Button1Click(Sender: TObject);
var
  aList : TStringList;
  i : Integer;
begin
  aList := TStringList.Create;
  Screen.Cursor := crHourGlass;
  try
    for i := 1 to 20 do aList.Add(IntToStr(i));
    DoSomeJob(aList);
  finally
    aList.Free;
    Screen.Cursor := crDefault;
  end;
end;

procedure TForm1.DoSomeJob(myListItems: TStringList);
const
  cThreadCount = 10;
  cMyQueueDepth = 100;
var
  i: Integer;
  aQueue: TThreadedQueue<TProc>;
  aCounter: TCountDownEvent;

  function CaptureJob(const aString: string): TProc;
  begin
    Result :=
      procedure
      var
        i,j : Integer;
      begin
        // Do some job with aString
        for i := 0 to 1000000 do
          j := i;
        // Report status to main thread
        TThread.Synchronize(nil,
          procedure
          begin
            Memo1.Lines.Add('Job with:'+aString+' done.');
          end
        );

      end;
  end;
var
  aThread : TThread;
begin
  aQueue := TThreadedQueue<TProc>.Create(cMyQueueDepth);
  aCounter := TCountDownEvent.Create(cThreadCount);
  try
    for i := 1 to cThreadCount do
      TMyConsumerItem.Create(aQueue, aCounter);
    for i := 0 to myListItems.Count - 1 do
    begin
      aQueue.PushItem(CaptureJob(myListItems[i]));
    end;
    // Kill the worker threads
    for i := 1 to cThreadCount do
      aQueue.PushItem(nil);
  finally
    // Since the worker threads synchronizes with the main thread,
    // we must wait for them in another thread.
    aThread := TThread.CreateAnonymousThread(
      procedure
      begin
        aCounter.WaitFor; // Wait for threads to finish
        aCounter.Free;
        aQueue.Free;
      end
    );
    aThread.FreeOnTerminate := false;
    aThread.Start;
    aThread.WaitFor;  // Safe to wait for the anonymous thread
    aThread.Free;
  end;
end;

end.