2013-03-25 66 views
6

我在delphi中遇到多线程问题。我有一个名称列表(约2000个名字),我需要在我的网站上获取每个名字的一些数据。除了线程控制外,我的系统完美运行。多线程Delphi

我想创建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名...

对不起。

+0

你可以发布TCheck ctor吗? – 2013-03-25 18:39:51

+5

另外,如果您想要10个线程,请制作10个线程,并让他们通过排队等待所有工作来处理您的工作。不要不断地创建/终止/销毁它们。忘掉tCount和微管理线程。 – 2013-03-25 18:42:07

+2

是的。你需要一个填充线程安全队列的生产者,以及10个消耗它的用户。 – 2013-03-25 19:31:53

回答

3

这里使用泛型的线程安全队列解决方案

定义多少个消费者th读取您想要的,队列深度,并从线程运行DoSomeJob过程。

定义您的工作与字符串一起作为通用程序(在CaptureJob)。

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

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

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:肯解释了为什么你的初始化和线程开始就错了。这个建议显示了一个更好的结构来以更通用的方式处理这类问题。

+0

这并不能解释问题的代码不起作用。在最初的问题中也没有说明使用了哪个版本的Delphi,所以不清楚泛型是否是一种选择。 (请参阅上面我的答案的最后两段。):-)(不是downvoting - 只是留下评论。) – 2013-03-25 22:42:19

+1

@KenWhite,对不起,这个答案只是为了说明如何用更好的结构解决这个问题。所有学分都发给你看海报代码中的错误。 – 2013-03-25 22:59:41

1

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

的正确方法是申报var Check: TCheck;MainT.Execute,然后存储返回值:

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,德尔福最新版本还支持TQueueTObjectQueue和一些其他类型和功能,可能是非常有帮助的。

(如果您有关于如何使用的线程数量有限,为此在队列中的不同的问题,这应该是一个新问题,不是你加入这一个。)