【问题标题】:Multi Thread Delphi [closed]多线程 Delphi [关闭]
【发布时间】:2013-03-25 18:37:39
【问题描述】:

我在 delphi 中遇到多线程问题。我有一个名称列表(大约 2.000 个名称),我需要获取我网站中每个名称的一些数据。我的系统运行良好,除了线程控制。

我想创建 10 个线程,当某个线程终止时,创建另一个……直到列表结束。

var
 Form1: TForm;
 tCount: Integer;  //threads count

implementation

type
 TCheck = class(TThread)
 public
  constructor Create(Name: string);
  destructor Destroy; Override;
 protected
  procedure Execute; Override;
 end;

 MainT = class(TThread)
 protected
  procedure Execute; Override;
 end;

destructor TCheck.Destroy;
begin
 Dec(tCount);
end;

procedure MainT.Execute;
var
 i: Integer;
 Load: TStringList;
begin
 Load:=TStringList.Create;
 Load.LoadFromFile('C:\mynames.txt');

 for i:= 0 to Load.Count -1 do
 begin

  if tCount = 10 then  //if we have 10 threads running...
  begin
   repeat
    Sleep(1);
   until tCount < 10;
  end;

  TCheck.Create(Load.Strings[i]);
  TCheck.Start;
  Inc(tCount);

 end;

end;  // end of procedure

好吧,我没有放 TCheck.Constructor,因为问题是我检查创建线程数的方法。我的意思是,我的软件只是停止,没有任何错误消息,有时检查 500 个名称,有时检查 150 个名称...

抱歉英语不好。

【问题讨论】:

  • 你能发布 TCheck ctor 吗?
  • 另外,如果您想要 10 个线程,请创建 10 个线程并让它们通过排队处理您的所有工作。不要不断地创建/终止/销毁它们。忘记 t​​Count 和微管理线程。
  • 是的。您需要一个填充线程安全队列的生产者和 10 个消耗它的消费者。
  • @Chris Thornton,线程安全 TStringList 的例子太可怕了 :)
  • @Abelisto '忘记 tCount 和微管理线程' - 这包括 tCount 不是线程安全的事实。使 tCount 线程安全的最好方法是根本不使用它。

标签: multithreading delphi


【解决方案1】:

这是一个使用泛型的线程安全队列解决方案。

定义您想要的消费者线程数、队列深度,然后从线程中运行DoSomeJob 过程。

将处理字符串的工作定义为通用过程(在CaptureJob 中)。

当队列为空时,消费者线程将被销毁。 DoSomeJob 过程一直等到所有作业都准备好。 您可以轻松地将其转换为通用工作池,重用线程而不破坏它们。工作项的通用结构也使其适合处理不同类型的工作。

请注意,此队列适用于 XE2 及更高版本。如果您使用的是较旧的 delphi 版本,请按照 cmets 中的建议查找类似的线程安全队列。

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>);
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;

NB:Ken 解释了为什么您的线程初始化和启动错误。该提案显示了一种更好的结构,可以以更通用的方式处理此类问题。

【讨论】:

  • 这并不能解释问题的代码如何不起作用。原始问题中也没有说明正在使用什么版本的 Delphi,因此不清楚泛型是否是一种选择。 (请参阅我上面答案的最后两段。):-) (不要投反对票 - 只是发表评论。)
  • @KenWhite,对不起,这个答案只是为了展示如何用更好的结构解决这个问题。感谢您发现海报代码中的错误。
【解决方案2】:

如果你没有声明一个变量来保存TCheck.Create 的返回值,你就不能访问TCheck.Start(没有TCheck 的实例可以用来访问Start 方法)。

正确的方法是在MainT.Execute 中声明一个var Check: TCheck;,然后存储返回的值:

Check := TCheck.Create(Load[i]);  { See note below }
Check.Start;
Inc(tCount);

注意 TStringList的默认属性是Strings,所以你不需要使用它。您可以像我上面所说的那样直接访问Strings。接下来的两行完全相同(但显然其中一行更短,更容易输入):

Load.Strings[i];
Load[i];

如果您不想保留对TCheck 的引用,只需将您的代码更改为with 块(包括begin..end,并且块中不包含其他代码(这是唯一的方式我曾经推荐使用with):

with TCheck.Create(Load[i]) do
begin
  Start;
  Inc(tCount);
end;

话虽如此,有更好的方法可以做到这一点,而不是创建/销毁各种线程。正如其他人所说,您可以有一个包含 10 个线程的列表并为它们排队工作,这样每个线程都会处理来自 Load 的项目,然后在完成后返回以获取另一个要处理的项目,并重复直到列表完成.很难确切地说你会怎么做,因为这取决于你的 Delphi 版本。 (有一些库可以为您完成大部分工作,例如 OMNIThreadLibrary,但它不适用于某些旧版本的 Delphi。Delphi 的最新版本还支持 TQueueTObjectQueue 以及其他一些类型和功能这可能会很有帮助。

(如果您对如何在线程数有限的队列中执行此操作有不同的问题,那应该是一个新问题,而不是您添加到此问题中的问题。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 2011-04-05
    • 1970-01-01
    • 2015-02-28
    • 2010-09-21
    • 1970-01-01
    相关资源
    最近更新 更多