2009-09-29 10 views
6

L'ho trovato sul sito Dr Dobbs oggi al http://www.ddj.com/hpc-high-performance-computing/220300055?pgno=3 È un bel suggerimento per quanto riguarda la procedura di filettatura. Qual è il modo migliore per raggiungere questo obiettivo con TThread in Delphi, mi chiedo? Grazie BrianCome programmare il numero di thread in Delphi

=== Da Dr Dobbs ==============

Effettuare multithreading configurabile! Il numero di thread utilizzati in un programma dovrebbe sempre essere configurabile da 0 (nessun thread aggiuntivo) a un numero arbitrario. Ciò non solo consente una personalizzazione per prestazioni ottimali, ma si rivela anche un ottimo strumento di debug e talvolta un vero toccasana quando si verificano condizioni di gara sconosciute sui sistemi client. Ricordo più di una situazione in cui i clienti erano in grado di superare bug fatali disattivando il multithreading. Questo ovviamente non si applica solo all'I/O di file multithread.

Si consideri il seguente pseudocodice:

int CMyThreadManger::AddThread(CThreadObj theTask) 
{ 
    if(mUsedThreadCount >= gConfiguration.MaxThreadCount()) 
     return theTask.Execute(); // execute task in main thread 
    // add task to thread pool and start the thread 
    ... 
} 

Tale meccanismo non è molto complicato (anche se un po 'più di lavoro sarà probabilmente necessaria quanto indicato qui), ma a volte è molto efficace. Può anche essere utilizzato con le librerie di thread predefinite come OpenMP o Intel's Threaded Building Blocks. Considerando le misure mostrate qui, è una buona idea includere più di un conteggio thread configurabile (ad esempio, uno per I/O di file e uno per le attività CPU core). Il valore predefinito potrebbe essere 0 per I/O file e < numero di core trovato > per le attività della CPU. Ma tutto il multithreading dovrebbe essere rimovibile. Un approccio più sofisticato potrebbe anche includere del codice per testare le prestazioni multithreading e impostare il numero di thread utilizzati automaticamente, anche singolarmente per attività diverse.

===================

risposta

0

Io in genere hanno una sola classe che eredita da TThread, uno che prende 'oggetti' lavoratore da una coda o una pila, e farli sospendere quando non ci sono più articoli disponibili. Il programma principale può quindi decidere il numero di istanze di questo thread da istanziare e avviare. (usando questo valore di configurazione).

Questa "coda di elementi di lavoro" dovrebbe anche essere sufficientemente intelligente per riprendere i thread sospesi o creare un nuovo thread quando richiesto (e quando il limite lo consente), quando un elemento di lavoro viene accodato o un thread ha terminato l'elaborazione di un elemento di lavoro .

+5

Non è necessario sospendere e riprendere i thread. Lascia che ogni thread attenda su un evento o semaforo, oppure usa 'WaitMessage()' con un loop di messaggi thread (potrebbe essere necessario per OLE). C'è molta discussione sull'argomento qui su SO sotto [delphi], leggilo, ma è meglio ignorare gli argomenti di persone che pensano di conoscere meglio degli sviluppatori di Embarcadero e di Microsoft, e provare a dirti di usare 'Suspend()' e 'Resume()' andrebbe bene. – mghie

5

Vorrei creare una classe astratta TTask. Questa classe ha lo scopo di eseguire l'attività. Con il metodo Esegui:

type 

    TTask = abstract class 
    protected 
    procedure DoExecute; virtual; abstract; 
    public 
    procedure Execute; 
    end; 

    TTaskThread = class (TThread) 
    private 
    FTask : TTask; 
    public 
    constructor Create(const ATask: TTask); 
    // Assigns FTask and enables thread, free on terminate. 

    procedure Execute; override; // Calls FTask.Execute. 
end; 

Il metodo Esegui verifica il numero di thread. Se non viene raggiunto il massimo, avvia un thread utilizzando TTaskThread che chiama DoExecute e come tale esegue l'attività in un thread. Se viene raggiunto il massimo, DoExecute viene chiamato direttamente.

4

The answer by Gamecat è buono per quanto riguarda la classe di attività astratta, ma penso che chiamare DoExecute() per un'attività nel thread chiamante (come anche l'articolo stesso) sia una cattiva idea. Metterei sempre in coda le attività da eseguire con i thread in background, a meno che il threading non fosse completamente disabilitato, ed ecco perché.

Si consideri il seguente caso (forzato), in cui è necessario eseguire tre procedure di CPU-bound indipendenti:

Procedure1_WhichTakes200ms; 
Procedure2_WhichTakes400ms; 
Procedure3_WhichTakes200ms; 

Per un migliore utilizzo del sistema dual core che si desidera eseguire loro in due thread. Limiteresti il ​​numero di thread in background a uno, quindi con il thread principale hai tutti i thread dei core.

Ora la prima procedura verrà eseguita in un thread di lavoro e terminerà dopo 200 millisecondi. La seconda procedura inizierà immediatamente e verrà eseguita nel thread principale, poiché il singolo thread di lavoro configurato è già occupato e terminerà dopo 400 millisecondi. Quindi l'ultima procedura verrà eseguita nel thread di lavoro, che ha già dormito per 200 millisecondi e terminerà dopo 200 millisecondi. Tempo di esecuzione totale 600 millisecondi e per 2/3 di quel tempo solo uno di entrambi i thread stava effettivamente facendo un lavoro significativo.

È possibile riordinare le procedure (attività), ma nella vita reale è probabilmente impossibile sapere in anticipo la durata di ciascuna attività.

Considerare ora il modo comune di utilizzare un pool di thread. Come per la configurazione si limiterà il numero di thread nel pool a 2 (numero di core), si utilizzerà il thread principale solo per pianificare i thread nel pool, quindi attendere che tutte le attività vengano completate. Con la suddetta sequenza di attività in coda, il thread 1 avrebbe eseguito la prima attività, il secondo due avrebbe eseguito la seconda attività. Dopo 200 millisecondi, la prima attività si completava e il primo thread di lavoro prendeva la terza attività dal pool, che è vuota in seguito. Dopo 400 millisecondi, la seconda e la terza attività sarebbero state completate e il thread principale sarebbe stato sbloccato. Tempo totale di esecuzione 400 millisecondi, con il 100% di carico su entrambi i core in quel momento.

Almeno per i thread associati alla CPU è di vitale importanza avere sempre il lavoro in coda per lo scheduler del SO. Chiamare DoExecute() nel thread principale interferisce con quello, e non dovrebbe essere fatto.

Problemi correlati