2010-08-05 13 views
58

Sembra che finalmente ho dovuto implementare una sorta di threading nel mio programma Delphi 2009. Se ci fosse solo un modo per farlo, sarei libero e funzionante. Ma vedo diverse possibilità.Come faccio a scegliere tra i vari modi di fare il threading in Delphi?

Qualcuno può spiegare qual è la differenza tra questi e perché sceglierei uno rispetto all'altro.

  1. La classe TThread in Delphi

  2. AsyncCalls da Andreas Hausladen

  3. OmniThreadLibrary da Primoz Gabrijelcic (gabr)

  4. ... tutti gli altri?


Edit:

Ho appena letto un eccellente articolo di Gabr nel marzo 2010 (No 10) rilascio di Blaise Pascal Magazine dal titolo "Quattro modi per creare un filo". Devi iscriverti per ottenere contenuti per la rivista, quindi per copyright, non posso riprodurre nulla di sostanziale su di esso qui.

In sintesi, Gabr descrive la differenza tra l'utilizzo di TThreads, le chiamate dirette all'API di Windows, Andy's AsyncCalls e la sua OmniThreadLibrary. Lo fa concludere alla fine che:

"Non sto dicendo che si deve scegliere altro che il modo classico di Delphi (TThread), ma è ancora buono per essere informato su opzioni che si hanno"

La risposta di Mghie è molto approfondita e suggerisce che OmniThreadLibrary potrebbe essere preferibile. Ma sono comunque interessato alle opinioni di tutti su come io (o chiunque altro) dovrei scegliere il loro metodo di threading per la loro applicazione.

E è possibile aggiungere all'elenco:

. 4. Chiamate dirette all'API di Windows

. 5. Misha Charrett'sCSI Distributed Application Framework come suggerito da LachlanG nella sua risposta.


Conclusione:

sto probabilmente intenzione di andare con OmniThreadLibrary. Mi piace il lavoro di Gabr. Ho usato il suo profiler GPProfile molti anni fa, e attualmente sto usando il suo GPStringHash che è in realtà parte di OTL.

La mia unica preoccupazione potrebbe essere l'aggiornamento per funzionare con l'elaborazione a 64 bit o Unix/Mac una volta che Embarcadero aggiunge tale funzionalità in Delphi.

risposta

41

Se non si ha esperienza con il multi-threading, probabilmente non si dovrebbe iniziare con TThread, poiché è un sottile strato sul thread nativo. Lo considero anche un po 'ruvido intorno ai bordi; non si è evoluto molto dall'introduzione con Delphi 2, principalmente modifiche per consentire la compatibilità con Linux nel frame di tempo Kylix e per correggere i difetti più evidenti (come la correzione della classe MREW rotta e infine la deprecazione di Suspend() e Resume() nell'ultima versione Versione Delphi).

L'utilizzo di una semplice classe di thread wrapper consente inoltre allo sviluppatore di concentrarsi su un livello troppo basso. Per fare un uso corretto di più core della CPU, è meglio concentrarsi sulle attività anziché sui thread, perché il partizionamento del lavoro con i thread non si adatta bene ai requisiti e agli ambienti in evoluzione, a seconda dell'hardware e dell'altro software che esegue in parallelo il numero ottimale di i thread possono variare notevolmente, anche in momenti diversi sullo stesso sistema. Una libreria a cui si passano solo blocchi di lavoro e che li programma automaticamente per sfruttare al meglio le risorse disponibili aiuta molto a questo proposito.

AsyncCalls è un buon primo passo per introdurre i thread in un'applicazione. Se nel tuo programma ci sono diverse aree in cui è necessario eseguire una serie di passaggi che sono indipendenti l'una dall'altra, è sufficiente eseguirle in modo asincrono trasferendole su AsyncCalls. Anche quando si dispone di una sola azione dispendiosa in termini di tempo, è possibile eseguirla in modo asincrono e mostrare semplicemente un'interfaccia utente di avanzamento nel thread VCL, facoltativamente consentendo di annullare l'azione.

AsyncCalls è IMO non è così buono per gli utenti in background che restano intorno durante l'intero runtime del programma e potrebbe essere impossibile da utilizzare quando alcuni degli oggetti nel programma hanno affinità di thread (come connessioni di database o oggetti OLE che potrebbero avere un requisito che tutte le chiamate avvengano nello stesso thread).

Ciò che è necessario tenere presente è che queste azioni asincrone sono non del tipo "fire-and-forget".Ogni funzione sovraccaricata di AsyncCall() restituisce un puntatore all'interfaccia IAsyncCall di cui potrebbe essere necessario conservare un riferimento se si desidera evitare il blocco. Se non si mantiene un riferimento, il momento in cui il conteggio ref raggiunge lo zero verrà liberata l'interfaccia, il che farà sì che il thread rilasciando l'interfaccia attenda il completamento della chiamata asincrona. Questo è qualcosa che potresti vedere durante il debug, quando uscire dal metodo che ha creato il IAsyncCall potrebbe richiedere una quantità di tempo misteriosa.

OTL è a mio parere la più versatile delle tre opzioni e la userei senza pensarci due volte. Può fare tutto TThread e AsyncCalls può fare, oltre a molto altro ancora. Ha un design sonoro, che è abbastanza di alto livello sia per rendere facile la vita per l'utente, sia per lasciare una porta su un sistema Unix (mantenendo intatta la maggior parte dell'interfaccia) almeno quanto meno possibile, se non addirittura facile. Negli ultimi mesi ha anche iniziato ad acquisire alcuni costrutti di alto livello per il lavoro parallelo, altamente raccomandato.

Anche OTL ha alcune dozzine di campioni, il che è importante per iniziare. AsyncCalls non ha che poche righe nei commenti, ma è abbastanza facile da capire a causa della sua funzionalità limitata (fa solo una cosa, ma lo fa bene). TThread ha un solo campione, che non è realmente cambiato in 14 anni ed è principalmente un esempio di come non fare le cose.

Qualsiasi delle opzioni scelte, nessuna libreria eliminerà la necessità di comprendere le nozioni di base sui thread. Avere letto un buon libro su questi è un prerequisito per qualsiasi codice di successo. Ad esempio, un blocco appropriato è un requisito per tutti loro.

+3

Ottima risposta. Sto appena iniziando ad esplorare l'aggiunta di threading alla mia applicazione che ha alcune conversazioni a volte lunghe con il mio database e voglio davvero che si fermi lì seduto come un oratore pubblico che ha perso la sua discussione. Avevo bisogno di questo tipo di panoramica. – jrodenhi

6

TThread è una classe semplice che incapsula un thread di Windows. Si crea una classe discendente con un metodo Execute che contiene il codice che deve essere eseguito da questo thread, crea il thread e lo imposta per l'esecuzione e il codice viene eseguito.

AsyncCalls e OmniThreadLibrary sono entrambe librerie che creano un concetto di livello superiore in cima ai thread.Sono circa attività, pezzi di lavoro discreti che è necessario eseguire in modo asincrono. Si avvia la libreria, si imposta un pool di attività, un gruppo di thread speciali il cui compito è di attendere finché non si lavora per loro e quindi si passa alla libreria un puntatore di funzione (o un puntatore del metodo o un metodo anonimo) contenente il codice che deve essere eseguito e lo esegue in uno dei thread del pool di attività e gestisce molti dei dettagli di basso livello per te.

Non ho usato entrambe le librerie tanto, quindi non posso davvero darti un confronto tra i due. Provali e vedi cosa possono fare, e quale ti senti meglio.

6

C'è un'altra libreria di threading Delphi meno conosciuta, Misha Charrett's CSI Application Framework.

Si basa sul passaggio del messaggio anziché sulla memoria condivisa. Lo stesso meccanismo di trasmissione dei messaggi viene utilizzato per comunicare tra thread in esecuzione nello stesso processo o in altri processi, quindi è una libreria di threading e una libreria di comunicazione tra processi distribuita.

C'è un po 'di una curva di apprendimento per iniziare, ma una volta che non ci si deve preoccupare di tutti i problemi di threading tradizionali come deadlock e sincronizzazione, il framework si occupa di gran parte di quello per voi.

Misha lo ha sviluppato per anni e sta ancora migliorando attivamente la struttura e la documentazione in ogni momento. È sempre molto reattivo nel sostenere le domande.

6

(mi dispiace, non ho abbastanza punti per lasciare un commento, così sto mettendo questo come una risposta piuttosto che un altro voto per OTL)

ho usato TThread, CSI e OmniThread (OTL). Le due librerie hanno entrambe curve di apprendimento non banali ma sono molto più capaci di TThread. La mia conclusione è che se stai per fare qualcosa di significativo con il threading finirai per scrivere metà della funzionalità della libreria, quindi potresti anche iniziare con la versione di lavoro, debuggata da qualcun altro. Sia Misha che Gabr sono programmatori migliori della maggior parte di noi, quindi le probabilità sono che abbiano fatto un lavoro migliore di quello che faremo.

Ho visto AsyncCalls ma non ha fatto abbastanza di quello che volevo. Una cosa che ha è una funzione "Sincronizza" (mancante da OTL) quindi se si dipende da quello si potrebbe andare con AynscCalls puramente per quello. L'IMO che utilizza il passaggio dei messaggi non è abbastanza difficile da giustificare la cattiveria di Sincronizzazione, quindi aggrappati e impara a usare i messaggi.

Dei tre preferisco OTL, in gran parte a causa della raccolta di esempi ma anche perché è più autonomo. Questo è meno di un problema se si sta già utilizzando il JCL o si lavora in un solo posto, ma io faccio un mix compreso il lavoro a contratto e la vendita di client sull'installazione del sistema di Misha è più difficile dell'OTL, solo perché l'OTL è ~ 20 file in una directory. Sembra sciocco, ma è importante per molte persone.

Con OTL la combinazione di cercare gli esempi e il codice sorgente per le parole chiave e fare domande nei forum funziona per me. Ho familiarità con i tradizionali processi "offload di CPU ad alta intensità di lavoro", ma in questo momento sto lavorando per creare un background di un lavoro di database che ha molto più "thread in attesa di DB" e meno "CPU al massimo", e l'OTL sta funzionando abbastanza bene per quello. Le differenze principali sono che posso avere più di 30 thread in esecuzione senza che la CPU si esaurisca, ma fermarne uno è generalmente impossibile.

1

So che questo non è il metodo più avanzato :-) e forse ha anche delle limitazioni, ma ho appena provato System. BeginThread e l'ho trovato abbastanza semplice, probabilmente a causa della qualità della documentazione a cui mi riferivo ...http://www.delphibasics.co.uk/RTL.asp?Name=BeginThread (IMO Neil Moffatt potrebbe insegnare MSDN una cosa o due)

Questo è il più grande fattore che trovo nel cercare di imparare cose nuove, la qualità della documentazione, non è quantità. Un paio d'ore sono bastate, poi sono tornato al vero lavoro piuttosto che preoccuparmi di come ottenere il filo per fare affari.

EDIT realtà Rob Kennedy fa un grande lavoro che spiega BeginThread qui BeginThread Structure - Delphi

EDIT in realtà il modo di Rob Kennedy spiega TThread nello stesso posto, penso che cambierò il mio codice per utilizzare TThread domani. Chissà come sarà la prossima settimana! (Probabilmente AsyncCalls)

+0

Sono completamente in disaccordo. È difficile immaginare una cosa peggiore da mostrare a un principiante piuttosto che chiamare le funzioni della VCL GUI al di fuori del thread principale. Il codice non imposta 'IsMultiThread' neanche. Se dopo questo si modella il proprio codice, allora lo scenario è impostato per alcuni bug difficili da rintracciare. – mghie

+0

@mghie, oh, ti riferisci all'esempio sulla pagina di base di Delphi. Beh, l'ho appena visto come un esempio di come avviare un thread separato. Non ho pensato a questo come necessariamente a favore di operazioni GUI dal thread. Sono contento di essermi imbattuto in esso. TThread, AsyncCalls o OTL mi avrebbero richiesto un paio d'ore in più (almeno) per ottenere ciò che volevo (che è 1: una semplice introduzione al multithreading e 2: un pezzo di codice funzionante così posso continuare con il resto di il mio lavoro piuttosto che passare l'intera giornata a giocherellare con i fili!). Ora posso imparare gli altri al mio ritmo, senza urgenza :-) – Sam

+0

@mghie, btw, avevo già letto che le operazioni della GUI dovrebbero essere limitate al thread principale, quindi in effetti è quello che ho fatto. Ho usato il thread per chiamare un servizio Web e reinserire i dati nel thread principale, che ha quindi aggiornato l'interfaccia utente. Funziona benissimo, per ora. – Sam

Problemi correlati