Cos'è la contesa del thread?


121

Qualcuno può spiegare semplicemente cos'è la contesa del thread?

L'ho cercato su Google, ma non riesco a trovare una semplice spiegazione.


10
Quindi scrivi qual è il tuo vago pensiero, così possiamo vedere dove potresti essere fuori, o la tua comprensione potrebbe essere corretta.
James Black

Risposte:


88

Essenzialmente il conflitto di thread è una condizione in cui un thread è in attesa di un blocco / oggetto che è attualmente tenuto da un altro thread. Questo thread in attesa pertanto non può utilizzare quell'oggetto finché l'altro thread non ha sbloccato quell'oggetto particolare.


53
Questa risposta è incompleta (come la maggior parte degli altri). Mentre un lucchetto è un tipo di cosa su cui ci possono essere controversie, è tutt'altro che l'unica cosa del genere. Possono esserci contese anche per le risorse senza blocco. (Ad esempio, se due thread continuano a incrementare atomicamente lo stesso numero intero, potrebbero verificarsi conflitti a causa del ping-pong della cache. Non sono coinvolti blocchi.)
David Schwartz

Nel caso di un Global Interpreter Lock (GIL) come in CPython, dove un thread deve sempre acquisire il GIL, più thread in esecuzione nello stesso processo sono quindi in conflitto per impostazione predefinita.
Acumenus

Penso che tu l'abbia spiegato in termini di Deadlock, ma è molto diverso da Deadlock.
Harshit Gupta

185

Diverse risposte sembrano concentrarsi sul conflitto di blocco, ma i blocchi non sono le uniche risorse su cui è possibile sperimentare il conflitto. La contesa è semplicemente quando due thread tentano di accedere alla stessa risorsa o alle risorse correlate in modo tale che almeno uno dei thread contendenti venga eseguito più lentamente di quanto sarebbe se gli altri thread non fossero in esecuzione.

L'esempio più ovvio di contesa è su un lucchetto. Se il thread A ha un blocco e il thread B desidera acquisire lo stesso blocco, il thread B dovrà attendere fino a quando il thread A non rilascia il blocco.

Ora, questo è specifico della piattaforma, ma il thread potrebbe subire rallentamenti anche se non deve mai aspettare che l'altro thread rilasci il blocco! Questo perché un blocco protegge qualche tipo di dati e spesso anche i dati stessi verranno contesi.

Ad esempio, si consideri un thread che acquisisce un blocco, modifica un oggetto, quindi rilascia il blocco e fa altre cose. Se due thread stanno facendo ciò, anche se non combattono mai per il blocco, i thread potrebbero essere eseguiti molto più lentamente di quanto farebbero se fosse in esecuzione un solo thread.

Perché? Supponiamo che ogni thread sia in esecuzione sul proprio core su una moderna CPU x86 e che i core non condividano una cache L2. Con un solo thread, l'oggetto può rimanere nella cache L2 la maggior parte del tempo. Con entrambi i thread in esecuzione, ogni volta che un thread modifica l'oggetto, l'altro thread troverà i dati non nella sua cache L2 perché l'altra CPU ha invalidato la riga della cache. Su un Pentium D, ad esempio, il codice verrà eseguito alla velocità FSB, che è molto inferiore alla velocità della cache L2.

Poiché la contesa può verificarsi anche se il blocco non viene contestato, la contesa può verificarsi anche quando non è presente alcun blocco. Ad esempio, supponiamo che la tua CPU supporti un incremento atomico di una variabile a 32 bit. Se un thread continua ad aumentare e diminuire una variabile, la variabile sarà calda nella cache per la maggior parte del tempo. Se due thread lo fanno, le loro cache si contenderanno la proprietà della memoria che contiene quella variabile e molti accessi saranno più lenti poiché il protocollo di coerenza della cache opera per proteggere ogni proprietà principale della linea cache.

Ironia della sorte, i blocchi in genere riducono la contesa. Perché? Perché senza un blocco, due thread potrebbero operare sullo stesso oggetto o raccolta e causare molti conflitti (ad esempio, ci sono code libere di blocco). I blocchi tenderanno a deschedule i thread contendenti, consentendo invece l'esecuzione di thread non contendenti. Se il thread A contiene un blocco e il thread B vuole lo stesso blocco, l'implementazione può invece eseguire il thread C. Se il thread C non ha bisogno di quel blocco, è possibile evitare future contese tra i thread A e B per un po '. (Ovviamente, questo presuppone che ci siano altri thread che potrebbero essere eseguiti. Non sarà d'aiuto se l'unico modo in cui il sistema nel suo insieme può fare progressi utili è eseguire thread che si contendono.)


4
+1 Inoltre, solo per rendere questo esplicito, le due variabili su cui due core stanno combattendo non hanno nemmeno bisogno di essere la stessa variabile per causare contese, devono solo essere archiviate in memoria nella stessa riga di cache. Riempire le strutture e / o allineare le strutture alla memoria può aiutare a evitare questa forma di contesa.
Rob_before_edits

1
@David per favore aiutaci a capire l'ultimo paragrafo della tua risposta in modo più dettagliato
Studente

4
@Naroji Fai una domanda al riguardo.
David Schwartz

@DavidSchwartz, sei un programmatore C?
Pacerier

@Pacerier C ++ principalmente.
David Schwartz

19

Da qui :

Si verifica un conflitto quando un thread attende una risorsa che non è immediatamente disponibile; rallenta l'esecuzione del codice, ma può risolversi nel tempo.

Un deadlock si verifica quando un thread è in attesa di una risorsa bloccata da un secondo thread e il secondo thread è in attesa di una risorsa bloccata dal primo thread. Più di due thread possono essere coinvolti in un deadlock. Un deadlock non si risolve mai da solo. Spesso provoca l'arresto dell'intera applicazione o della parte in cui si verifica il deadlock.


Questo spiega anche la differenza tra thread Contention e Deadlock
Sankalp

3

Penso che dovrebbero esserci dei chiarimenti dall'OP sullo sfondo della domanda - posso pensare a 2 risposte (anche se sono sicuro che ci sono aggiunte a questo elenco):

  1. se ti riferisci al "concetto" generale di contesa di thread e come può presentarsi in un'applicazione, rimando alla risposta dettagliata di @ DavidSchwartz sopra.

  2. C'è anche il contatore delle prestazioni '.NET CLR Locks and Threads: Total # of Contentions'. Come tratto dalla descrizione PerfMon di questo contatore, è definito come:

    Questo contatore visualizza il numero totale di volte in cui i thread in CLR hanno tentato di acquisire un blocco gestito senza successo. I blocchi gestiti possono essere acquisiti in molti modi; dall'istruzione "lock" in C # o chiamando System.Monitor.Enter o utilizzando l'attributo personalizzato MethodImplOptions.Synchronized.

... e sono sicuro che altri per altri sistemi operativi e framework applicativi.


2

Hai 2 thread. Thread A e Thread B, hai anche l'oggetto C.

A sta attualmente accedendo all'oggetto C e ha posizionato un blocco su quell'oggetto. B deve accedere all'oggetto C, ma non può farlo finché A non rilascia il blocco sull'oggetto C.


1

Un'altra parola potrebbe essere concorrenza. È semplicemente l'idea di due o più thread che cercano di utilizzare la stessa risorsa.


1

Per me la contesa è una competizione tra 2 o più thread su una risorsa condivisa. La risorsa può essere un lucchetto, un contatore, ecc. Competizione significa "chi la ottiene per primo". Più thread ci sono più contese. Più frequente è l'accesso a una risorsa, maggiore è la contesa.


1

Immagina il seguente scenario. Ti stai preparando per l'esame finale di domani e ti senti un po 'affamato. Quindi, dai dieci dollari a tuo fratello minore e gli chiedi di comprarti una pizza. In questo caso, tu sei il filo principale e tuo fratello è un filo bambino. Una volta che l'ordine è stato dato, sia tu che tuo fratello state facendo il loro lavoro contemporaneamente (cioè studiando e comprando una pizza). Ora, dobbiamo considerare due casi. Per prima cosa, tuo fratello riporta la tua pizza e finisce mentre tu studi. In questo caso, puoi smettere di studiare e goderti la pizza. In secondo luogo, finisci lo studio presto e dormi (ovvero, il lavoro assegnato per oggi - studio per l'esame finale di domani - è fatto) prima che la pizza sia disponibile. Ovviamente non puoi dormire; altrimenti non avrai la possibilità di mangiare la pizza.

Come nell'esempio, i due casi danno un significato di rivalità.


0

Il conflitto di thread è influenzato anche dalle operazioni di I / O. Esempio quando un thread in attesa di lettura del file può essere considerato come un conflitto. Usa le porte di completamento I / O come soluzione.


0

Il conflitto di blocco si verifica quando un thread tenta di acquisire il blocco su un oggetto che è già stato acquisito da un altro thread *. Fino a quando l'oggetto non viene rilasciato, il thread è bloccato (in altre parole, è nello stato Waiting). In alcuni casi, ciò può portare a una cosiddetta esecuzione seriale che influisce negativamente sull'applicazione.

dalla documentazione di dotTrace

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.