2013-03-25 15 views
6

Mam problemy z wieloma wątkami w delphi. Mam listę nazwisk (coś około 2000 nazwisk) i muszę uzyskać dane o każdym nazwisku w mojej witrynie. Mój system działa idealnie, z wyjątkiem kontroli wątków.Wiele wątków Delphi

Chcę utworzyć 10 wątków, a gdy jakiś wątek się zakończy, utwórz kolejny ... do końca listy.

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 

Cóż, ja nie wprowadził TCheck.Constructor ponieważ problemem jest sposób w jaki jestem sprawdzić liczbę utworzonych wątków. Mam na myśli, że moje oprogramowanie przestało działać, bez żadnego komunikatu o błędzie, czasami sprawdzam nazwy 500, czasem 150 nazw ...

Przepraszamy za zły angielski.

+0

Możesz napisać TCheck ctor? –

+5

Ponadto, jeśli chcesz mieć 10 wątków, zrób 10 wątków i każ im przetwarzać całą pracę, kolejkując do nich rzeczy. Nie twórz ich nieustannie/zakończ/zniszcz. Zapomnij o wątkach tCount i mikro-zarządzaniu. –

+2

Tak. Potrzebujesz producenta, który zapełni kolejkę wątków i 10 konsumentów, którzy go wyrzucą. –

Odpowiedz

3

Oto rozwiązanie kolejki wątków używające generycznych

Określ, ilu klientów żąda odczytu, głębokości kolejki i po prostu uruchamia procedurę DoSomeJob z wątku.

Określ swoją pracę, pracując z ciągiem jako procedurą generyczną (w CaptureJob).

Gdy kolejka jest pusta, wątki konsumenckie zostaną zniszczone. Procedura DoSomeJob czeka, aż wszystkie zadania będą gotowe. Możesz łatwo przekształcić to w zwykłą pulę roboczą, ponownie wykorzystując wątki bez niszczenia ich. Ogólna struktura elementów pracy sprawia, że ​​są one odpowiednie do różnych rodzajów pracy.

Uwaga ta kolejka działa na XE2 i nowszych. Jeśli korzystasz ze starszej wersji delphi, poszukaj podobnej kolejki wątków zgodnie z sugestiami w komentarzach.

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 wyjaśnia, dlaczego inicjalizacji i startu wątków są błędne. Ta propozycja pokazuje lepszą strukturę, aby poradzić sobie z tego typu problemami w bardziej ogólny sposób.

+0

To nie wyjaśnia, w jaki sposób kod pytania nie działa. W oryginalnym pytaniu o to, która wersja Delphi jest używana, nie ma również wskazania, więc nie jest jasne, czy generyczne są opcją. (Zobacz dwa ostatnie akapity mojej odpowiedzi powyżej.) :-) (Not downvoting - just leaving a comment.) –

+1

@KenWhite, przepraszam, ta odpowiedź miała na celu tylko pokazanie jak ten problem mógł zostać rozwiązany dzięki lepszej strukturze. Wszystkie kredyty dla ciebie za wykrycie błędu w kodzie plakatów. –

1

Jeśli nie zadeklarować zmienną do przechowywania wartości zwracanej TCheck.Create, nie można uzyskać dostępu TCheck.Start (nie ma instancja TCheck można użyć, aby uzyskać dostęp do metody Start).

Właściwym sposobem byłoby zadeklarować var Check: TCheck; wewnątrz MainT.Execute, a następnie zapisać wartość zwracana:

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

UWAGA Domyślna właściwość TStringList jest Strings, więc nie trzeba go używać . Możesz po prostu uzyskać dostęp do Strings bezpośrednio, tak jak powyżej. Kolejne dwie linie są dokładnie to samo (ale oczywiście jedna jest krótsza i łatwiejsza do pisania):

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

Jeśli nie chcesz zachować odniesienie do TCheck, wystarczy zmienić kod być with blok (w tym begin..end i nie zawierający inny kod w bloku (jest to jedynym sposobem kiedykolwiek zalecamy użycie with):

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

Mając to na uwadze powyższe, istnieje wiele lepszych sposobów można to zrobić zamiast tworzyć/niszczyć wszystkie rodzaje czyta. Jak powiedzieli inni, możesz mieć listę 10 wątków i umieścić je w kolejce, aby każdy mógł przetworzyć przedmiot z Load, a następnie wrócić, by otrzymać kolejny element do przetworzenia po zakończeniu, i powtarzać, aż lista zostanie ukończona. Trudno dokładnie powiedzieć, jak byś to zrobił, ponieważ to zależy od twojej wersji Delphi. (Dostępne są biblioteki, które wykonają większość pracy dla Ciebie, np. OMNIThreadLibrary, ale nie są dostępne dla niektórych starszych wersji Delphi. Najnowsze wersje Delphi obsługują również TQueue i TObjectQueue oraz kilka innych typów i funkcji, które mogą być bardzo pomocne.

(Jeśli masz inne pytanie o tym, jak to zrobić w kolejce z ograniczonej liczby wątków, które powinny być nowe pytanie, nie coś dodać do tego.)

Powiązane problemy