2013-03-25 14 views
6

Ho problemi con multi thread in delphi. Ho una lista di nomi (qualcosa su 2.000 nomi), e ho bisogno di ottenere alcuni dati di ciascun nome nel mio sito. Il mio sistema funziona perfettamente, tranne il controllo del thread.Multi Thread Delphi

Voglio creare 10 thread e, quando alcuni thread terminano, creare un altro ... fino alla fine della lista.

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 

Beh, non ho messo il TCheck.Constructor perché il problema è il metodo come sto verificare il numero di thread creati. Voglio dire, il mio software si ferma, senza alcun messaggio di errore, a volte controlla 500 nomi, a volte 150 nomi ...

Ci scusiamo per il cattivo inglese.

+0

Potrebbe pubblicare TCheck Ctor? –

+5

Inoltre, se vuoi 10 thread, crea 10 thread e fai in modo che tutto il tuo lavoro venga processato accodandoli a loro. Non creare/terminare/distruggere continuamente. Dimentica i thread di tCount e micro-gestione. –

+2

Sì. Hai bisogno di un produttore che riempia una coda protetta da thread e 10 utenti che la scaricano. –

risposta

3

Ecco una soluzione in coda per l'uso di codici generici

Definire quanti utenti legge quello che vuoi, la profondità della coda ed esegui semplicemente la procedura DoSomeJob da una discussione.

Definire il lavoro con una stringa come procedura generica (in CaptureJob).

Quando la coda è vuota, i thread dei consumatori verranno distrutti. La procedura DoSomeJob attende fino a quando tutti i lavori sono pronti. È possibile trasformarlo facilmente in un pool generico di worker, riutilizzando i thread senza distruggerli. La struttura generica degli oggetti di lavoro li rende anche adatti a gestire diversi tipi di lavoro.

Nota questa coda funziona su XE2 e versioni successive. Se si utilizza una versione delphi precedente, cercare una coda threadsafe simile a quella suggerita nei commenti.

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 spiega perché il tuo l'inizializzazione e l'avvio di discussioni sono sbagliate. Questa proposta mostra una struttura migliore per gestire questo tipo di problemi in un modo più generico.

+0

Questo non spiega come il codice della domanda non funzioni. Inoltre, non vi è alcuna indicazione nella domanda iniziale su quale versione di Delphi viene utilizzata, quindi non è chiaro che i generici siano un'opzione. (Vedi gli ultimi due paragrafi della mia risposta sopra.) :-) (Non downvoting - solo lasciando un commento.) –

+1

@KenWhite, scusa, questa risposta è stata solo per mostrare come questo problema potrebbe essere risolto con una struttura migliore. Tutti i crediti per individuare l'errore nel codice del poster. –

1

Se non si dichiara una variabile per contenere il valore di ritorno di TCheck.Create, non è possibile accedere TCheck.Start (non c'è nessuna istanza di TCheck è possibile utilizzare per accedere al metodo Start).

Il modo corretto sarebbe quello di dichiarare un var Check: TCheck; all'interno MainT.Execute, e quindi memorizzare il valore restituito:

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

NOTA La proprietà predefinita di TStringList è Strings, quindi non c'è bisogno di usarlo . Puoi semplicemente accedere a Strings direttamente come sopra. Le prossime due linee sono esattamente la stessa cosa (ma ovviamente uno è più breve e più facile da digitare):

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

Se non si desidera mantenere un riferimento a TCheck, basta cambiare il codice per essere un blocco with (compreso il begin..end, e che non contiene altro codice nel blocco (questa è l'unico modo ho mai consiglia di utilizzare un with):

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

Con questo detto, ci sono modi molto migliori si può fare questo invece di creare/distruggere tutti i tipi di th legge. Come altri hanno già detto, è possibile avere un elenco di 10 thread e accodare il lavoro per loro, in modo che ognuno possa elaborare un articolo da Load e quindi tornare per ottenere un altro elemento da elaborare una volta terminato, e ripetere fino a quando l'elenco non è completo. È difficile dire esattamente come lo faresti, perché ciò dipenderebbe dalla tua versione Delphi. (Esistono librerie disponibili che faranno la maggior parte del lavoro per te, come OMNIThreadLibrary, ma non è disponibile per alcune versioni precedenti di Delphi. Le versioni recenti di Delphi supportano anche TQueue e TObjectQueue e alcuni altri tipi e funzionalità che potrebbero essere molto utili.

(Se hai una domanda diversa su come fare questo in una coda con un numero limitato di fili, che dovrebbe essere una nuova domanda , non qualcosa che si aggiunge a questo.)