Qual è il significato del termine "thread-safe"?


367

Significa che due thread non possono cambiare contemporaneamente i dati sottostanti? Oppure significa che il segmento di codice specificato verrà eseguito con risultati prevedibili quando più thread eseguono quel segmento di codice?


8
Ho appena visto un'interessante discussione qui su questo argomento: blogs.msdn.com/ericlippert/archive/2009/10/19/…
Sebastian

Risposte:


256

Da Wikipedia:

La sicurezza del thread è un concetto di programmazione informatica applicabile nel contesto di programmi multi-thread. Un pezzo di codice è sicuro per i thread se funziona correttamente durante l'esecuzione simultanea di più thread. In particolare, deve soddisfare la necessità che più thread accedano agli stessi dati condivisi e la necessità di accedere a un dato condiviso a un solo thread alla volta.

Esistono alcuni modi per ottenere la sicurezza del thread:

Re-entrancy:

Scrivere il codice in modo tale che possa essere parzialmente eseguito da un'attività, reinserito da un'altra attività e quindi ripreso dall'attività originale. Ciò richiede il salvataggio delle informazioni sullo stato nelle variabili locali per ciascuna attività, generalmente nel suo stack, anziché nelle variabili statiche o globali.

Esclusione reciproca:

L'accesso ai dati condivisi viene serializzato mediante meccanismi che assicurano che un solo thread legga o scriva i dati condivisi in qualsiasi momento. È necessaria molta attenzione se un pezzo di codice accede a più parti condivise di dati: i problemi includono condizioni di competizione, deadlock, livelock, fame e vari altri mali elencati in molti libri di testo di sistemi operativi.

Archivio locale thread:

Le variabili sono localizzate in modo che ogni thread abbia la propria copia privata. Queste variabili mantengono i loro valori oltre i limiti della subroutine e di altri codici e sono thread-safe poiché sono locali per ciascun thread, anche se il codice a cui accedono potrebbe essere rientrante.

Operazioni atomiche:

È possibile accedere ai dati condivisi utilizzando operazioni atomiche che non possono essere interrotte da altri thread. Questo di solito richiede l'uso di speciali istruzioni sul linguaggio macchina, che potrebbero essere disponibili in una libreria di runtime. Poiché le operazioni sono atomiche, i dati condivisi vengono sempre mantenuti in uno stato valido, indipendentemente da quali altri thread vi accedano. Le operazioni atomiche costituiscono la base di molti meccanismi di bloccaggio del filo.

leggi di più:

http://en.wikipedia.org/wiki/Thread_safety



4
Questo collegamento, tecnicamente, manca diversi punti critici. La memoria condivisa in questione deve essere mutabile (la memoria di sola lettura non può essere thread-non sicura) e i thread multipli devono, a) eseguire più operazioni di scrittura sulla memoria durante la quale la memoria si trova in modo incoerente (errato) stato eb) consentire ad altri thread di interrompere quel thread mentre la memoria è incoerente.
Charles Bretana,

20
quando cercato, il primo risultato di Google è wiki, non serve a niente ridurlo qui.
Ranvir,

Cosa intendi con "un codice che accede a una funzione"? La stessa funzione eseguita è il codice, non è vero?
Koray Tugay,

82

Il codice thread-safe è un codice che funzionerà anche se molti thread lo eseguono contemporaneamente.

http://mindprod.com/jgloss/threadsafe.html


34
Nello stesso processo!
Ali Afshar,

Anzi, nello stesso processo :)
Marek Blotny,

4
"Scrivere codice che durerà stabilmente per settimane richiede un'estrema paranoia." Questa è una citazione che mi piace :)
Jim T,

5
duh! questa risposta ribadisce la domanda! --- E perché solo nell'ambito dello stesso processo ??? Se il codice fallisce quando più thread lo eseguono da processi diversi, quindi, probabilmente (la "memoria condivisa" potrebbe trovarsi in un file su disco), NON è thread thread !!
Charles Bretana,

1
@ mg30rg. Forse la confusione è il risultato di pensare in qualche modo che quando un blocco di codice viene eseguito da più processi, ma solo da un thread per processo, quello, in qualche modo è ancora uno scenario "Single-threaded", non uno scenario multi-thread . Questa idea non è nemmeno sbagliata. È solo una definizione errata. Chiaramente, più processi generalmente non vengono eseguiti sullo stesso thread in modo sincronizzato (tranne in rari scenari in cui i processi di progettazione si coordinano tra loro e il sistema operativo condivide i thread tra i processi.)
Charles Bretana,

50

Una domanda più istruttiva è ciò che rende il codice non thread sicuro e la risposta è che ci sono quattro condizioni che devono essere vere ... Immagina il seguente codice (ed è la traduzione automatica della lingua)

totalRequests = totalRequests + 1
MOV EAX, [totalRequests]   // load memory for tot Requests into register
INC EAX                    // update register
MOV [totalRequests], EAX   // store updated value back to memory
  1. La prima condizione è che vi siano posizioni di memoria accessibili da più di un thread. In genere, queste posizioni sono variabili globali / statiche o sono memoria heap raggiungibile da variabili globali / statiche. Ogni thread ottiene il proprio frame di stack per le variabili locali con ambito funzione / metodo, quindi queste variabili di metodo / funzione locali, otoh, (che sono nello stack) sono accessibili solo dall'unico thread che possiede quello stack.
  2. La seconda condizione è che esiste una proprietà (spesso chiamata invariante ), associata a queste posizioni di memoria condivisa, che deve essere vera o valida per il corretto funzionamento del programma. Nell'esempio sopra, la proprietà è che " totalRequests deve rappresentare accuratamente il numero totale di volte in cui un thread ha eseguito qualsiasi parte dell'istruzione di incremento ". In genere, questa proprietà invariante deve essere vera (in questo caso, totalRequests deve contenere un conteggio accurato) prima che si verifichi un aggiornamento affinché l'aggiornamento sia corretto.
  3. La terza condizione è che la proprietà invariante NON sia valida durante alcune parti dell'aggiornamento effettivo. (È temporaneamente non valido o falso durante una parte dell'elaborazione). In questo caso particolare, dal momento in cui viene recuperato totalRequests fino al momento in cui viene memorizzato il valore aggiornato, totalRequests non soddisfa l'invariante.
  4. La quarta e ultima condizione che deve verificarsi affinché si verifichi una corsa (e affinché il codice NON sia "thread-safe") è che un altro thread deve essere in grado di accedere alla memoria condivisa mentre l'invariante è rotto, causando in tal modo incoerente o comportamento scorretto.

6
Questo copre solo ciò che è noto come data race , ed è ovviamente importante. Tuttavia, ci sono altri modi in cui il codice non può essere thread-safe, ad esempio un blocco errato che può causare deadlock. Anche qualcosa di semplice come chiamare System.exit () da qualche parte in un thread Java non rende sicuro quel codice.
Ingo,

2
Immagino che in qualche modo si tratti di semantica, ma direi che un cattivo codice di blocco che può causare un deadlock non rende il codice non sicuro. Innanzitutto, non è necessario bloccare il codice in primo luogo a meno che non sia possibile una condizione di competizione, come descritto sopra. Quindi, se si scrive il codice di blocco in modo tale da causare un deadlock, questo non è un thread non sicuro, è solo un codice errato.
Charles Bretana,

1
Si noti tuttavia che il deadlock non si verifica quando si esegue il thread singolo, quindi per la maggior parte di noi questo rientra sicuramente nel significato intuitivo di (non) "thread-safe".
Jon Coombs,

Bene, i deadlock non possono verificarsi a meno che tu non stia eseguendo il multi-thread ovviamente, ma è come dire che i problemi di rete non possono verificarsi se si esegue su un computer. Altri problemi possono verificarsi anche a thread singolo, se il programmatore scrive il codice in modo che fuoriesca dalle righe critiche di codice prima di completare l'aggiornamento e modifica la variabile in qualche altra subroutine.
Charles Bretana,

34

Mi piace la definizione dalla concorrenza Java in pratica di Brian Goetz per la sua completezza

"Una classe è sicura per i thread se si comporta correttamente quando vi si accede da più thread, indipendentemente dalla pianificazione o dall'interleaving dell'esecuzione di tali thread da parte dell'ambiente di runtime e senza alcuna sincronizzazione aggiuntiva o altro coordinamento da parte del codice chiamante. "


Questa definizione è incompleta e non specifica e sicuramente non esaustiva. Quante volte deve funzionare in sicurezza, solo una volta? dieci volte? ogni volta? L'80% delle volte? e non specifica ciò che lo rende "non sicuro". Se non riesce a funzionare in modo sicuro, ma l'errore è dovuto al fatto che c'è una divisione per zero errore, lo rende thread- "Unsafe"?
Charles Bretana,

Sii più civile la prossima volta e forse possiamo discuterne. Questo non è Reddit e non ho voglia di parlare con persone maleducate.
Buu Nguyen,

Interpretano i commenti sulla definizione di qualcun altro come insulti a te stesso. Devi leggere e comprendere la sostanza prima di reagire emotivamente. Nulla di incivile sul mio commento. Stavo sottolineando il significato della definizione. Scusa se gli esempi che ho usato per illustrare il punto ti hanno messo a disagio.
Charles Bretana,

28

Come altri hanno sottolineato, la sicurezza del thread significa che un pezzo di codice funzionerà senza errori se viene utilizzato da più di un thread alla volta.

Vale la pena sapere che a volte questo ha un costo, del tempo del computer e di una codifica più complessa, quindi non è sempre desiderabile. Se una classe può essere utilizzata in modo sicuro su un solo thread, potrebbe essere meglio farlo.

Ad esempio, Java ha due classi quasi equivalenti StringBuffere StringBuilder. La differenza è che StringBufferè thread-safe, quindi una singola istanza di a StringBufferpuò essere utilizzata da più thread contemporaneamente. StringBuildernon è thread-safe, ed è progettato come un sostituto di prestazioni più elevate per quei casi (la stragrande maggioranza) quando String è costruito da un solo thread.


22

Il thread-safe-code funziona come specificato, anche se inserito contemporaneamente da thread diversi. Ciò significa spesso che strutture di dati o operazioni interne che devono essere eseguite senza interruzioni sono protette da diverse modifiche contemporaneamente.


21

Un modo più semplice per capirlo, è ciò che rende il codice non thread-safe. Esistono due problemi principali che faranno in modo che un'applicazione filettata abbia un comportamento indesiderato.

  • Accesso alla variabile condivisa senza blocco
    Questa variabile può essere modificata da un altro thread durante l'esecuzione della funzione. Vuoi prevenirlo con un meccanismo di blocco per essere sicuro del comportamento della tua funzione. La regola generale è quella di mantenere il blocco il più breve tempo possibile.

  • Deadlock causato dalla dipendenza reciproca dalla variabile condivisa
    Se si hanno due variabili condivise A e B. In una funzione, si blocca prima A e poi si blocca B. In un'altra funzione, si avvia il blocco B e dopo un po 'si blocca A. Questo è un potenziale deadlock in cui la prima funzione attenderà lo sblocco di B quando la seconda funzione attenderà lo sblocco di A. Questo problema probabilmente non si verificherà nel tuo ambiente di sviluppo e solo di volta in volta. Per evitarlo, tutti i blocchi devono essere sempre nello stesso ordine.


9

Sì e no.

La sicurezza del thread è un po 'più che assicurarsi che i tuoi dati condivisi siano accessibili solo da un thread alla volta. Devi garantire l'accesso sequenziale ai dati condivisi, evitando allo stesso tempo condizioni di gara , deadlock , livelock e fame di risorse .

Risultati imprevedibili quando sono in esecuzione più thread non è una condizione obbligatoria del codice thread-safe, ma spesso è un sottoprodotto. Ad esempio, potresti avere uno schema produttore-consumatore impostato con una coda condivisa, un thread produttore e pochi thread consumatore e il flusso di dati potrebbe essere perfettamente prevedibile. Se inizi a presentare più consumatori vedrai risultati più casuali.


9

In sostanza, molte cose possono andare storte in un ambiente multi-thread (istruzioni di riordino, oggetti parzialmente costruiti, stessa variabile con valori diversi in thread diversi a causa della memorizzazione nella cache a livello di CPU ecc.).

Mi piace la definizione data da Java Concurrency in Practice :

Una [porzione di codice] è thread-safe se si comporta correttamente quando vi si accede da più thread, indipendentemente dalla pianificazione o dall'interleaving dell'esecuzione di quei thread da parte dell'ambiente di runtime e senza alcuna sincronizzazione aggiuntiva o altro coordinamento da parte del codice chiamante.

Indicano correttamente che il programma si comporta in conformità con le sue specifiche.

Esempio contrastato

Immagina di implementare un contatore. Si potrebbe dire che si comporta correttamente se:

  • counter.next() non restituisce mai un valore che è già stato restituito in precedenza (non presupponiamo un overflow ecc. per semplicità)
  • tutti i valori da 0 al valore corrente sono stati restituiti ad un certo punto (nessun valore viene ignorato)

Un contatore sicuro dei thread si comporterebbe in base a tali regole indipendentemente da quanti thread vi accedono contemporaneamente (cosa che in genere non sarebbe il caso di un'implementazione ingenua).

Nota: post-cross sui programmatori


8

Semplicemente: il codice funzionerà correttamente se molti thread eseguono questo codice contemporaneamente.


5

Non confondere la sicurezza del thread con il determinismo. Il codice thread-safe può anche essere non deterministico. Data la difficoltà di debug dei problemi con il codice thread, questo è probabilmente il caso normale. :-)

La sicurezza del thread garantisce semplicemente che quando un thread modifica o legge i dati condivisi, nessun altro thread può accedervi in ​​modo da modificare i dati. Se il tuo codice dipende da un certo ordine per l'esecuzione per correttezza, allora hai bisogno di altri meccanismi di sincronizzazione oltre a quelli richiesti per la sicurezza del thread per garantire questo.


5

Vorrei aggiungere qualche informazione in più su altre buone risposte.

La sicurezza del thread implica che più thread possono scrivere / leggere dati nello stesso oggetto senza errori di incoerenza della memoria. In un programma multi-thread, un programma thread-safe non causa effetti collaterali ai dati condivisi .

Dai un'occhiata a questa domanda SE per maggiori dettagli:

Cosa significa threadsafe?

Il programma thread safe garantisce la coerenza della memoria .

Dalla pagina della documentazione di Oracle sull'API simultanea avanzata:

Proprietà di coerenza della memoria:

Il capitolo 17 di Java ™ Language Specification definisce la relazione accidentale sulle operazioni di memoria come letture e scritture di variabili condivise. I risultati di una scrittura di un thread sono garantiti per essere visibili a una lettura di un altro thread solo se l'operazione di scrittura avviene prima dell'operazione di lettura .

I costrutti synchronizede volatile, così come i metodi Thread.start()e Thread.join(), possono formare relazioni che accadono prima .

I metodi di tutte le classi java.util.concurrente dei relativi pacchetti secondari estendono queste garanzie alla sincronizzazione di livello superiore. In particolare:

  1. Le azioni in un thread prima di posizionare un oggetto in qualsiasi raccolta simultanea avvengono prima delle azioni successive all'accesso o alla rimozione di quell'elemento dalla raccolta in un altro thread.
  2. Azioni in un thread prima dell'invio di un Runnablea Executoraccadere prima dell'inizio della sua esecuzione. Allo stesso modo per Callables sottoposti a un ExecutorService.
  3. Azioni eseguite dal calcolo asincrono rappresentato da Futureazioni precedenti prima del recupero del risultato tramite Future.get()un altro thread.
  4. Azioni prima del "rilascio" di metodi di sincronizzazione come le Lock.unlock, Semaphore.release, and CountDownLatch.countDownazioni che si verificano prima di un metodo di "acquisizione" riuscito come Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.awaitsullo stesso oggetto sincronizzatore in un altro thread.
  5. Per ogni coppia di thread che scambiano correttamente oggetti tramite un Exchanger, si verificano azioni precedenti a exchange()in ogni thread, prima di quelle successive allo scambio corrispondente () in un altro thread.
  6. Le azioni prima della chiamata CyclicBarrier.awaite Phaser.awaitAdvance(oltre alle sue varianti) si verificano prima delle azioni eseguite dall'azione barriera, e le azioni eseguite dall'azione barriera si verificano prima delle azioni successive a un ritorno riuscito dall'attesa corrispondente in altri thread.

4

Per completare altre risposte:

La sincronizzazione è solo una preoccupazione quando il codice nel tuo metodo fa una delle due cose:

  1. funziona con alcune risorse esterne che non sono thread-safe.
  2. Legge o modifica un oggetto persistente o un campo di classe

Ciò significa che le variabili definite ENTRO il tuo metodo sono sempre thread-safe. Ogni chiamata a un metodo ha la sua versione di queste variabili. Se il metodo viene chiamato da un altro thread o dallo stesso thread o anche se il metodo chiama se stesso (ricorsione), i valori di queste variabili non vengono condivisi.

La pianificazione dei thread non è garantita come round robin . Un'attività può impadronirsi totalmente della CPU a spese di thread con la stessa priorità. Puoi usare Thread.yield () per avere una coscienza. Puoi usare (in java) Thread.setPriority (Thread.NORM_PRIORITY-1) per ridurre la priorità di un thread

Inoltre attenzione a:

  • il grande costo di runtime (già citato da altri) per le applicazioni che ripetono queste strutture "thread-safe".
  • Thread.sleep (5000) dovrebbe dormire per 5 secondi. Tuttavia, se qualcuno cambia l'ora del sistema, potresti dormire per un tempo molto lungo o per niente. Il sistema operativo registra il tempo di sveglia in forma assoluta, non relativa.

2

Si e si. Implica che i dati non vengano modificati contemporaneamente da più di un thread. Tuttavia, il programma potrebbe funzionare come previsto e apparire thread-safe, anche se fondamentalmente non lo è.

Si noti che l'imprevedibilità dei risultati è una conseguenza delle "condizioni di gara" che probabilmente comportano la modifica dei dati in un ordine diverso da quello previsto.


2

Rispondiamo a questo esempio:

class NonThreadSafe {

    private int counter = 0;

    public boolean countTo10() {
        count = count + 1;
        return (count == 10);
    }

Il countTo10metodo aggiunge uno al contatore e quindi restituisce vero se il conteggio ha raggiunto 10. Dovrebbe restituire vero solo una volta.

Questo funzionerà fintanto che un solo thread esegue il codice. Se due thread eseguono il codice contemporaneamente possono verificarsi vari problemi.

Ad esempio, se il conteggio inizia come 9, un thread potrebbe aggiungere 1 al conteggio (creazione di 10), ma un secondo thread potrebbe immettere il metodo e aggiungere nuovamente 1 (creazione di 11) prima che il primo thread abbia la possibilità di eseguire il confronto con 10 Quindi entrambi i thread fanno il confronto e scoprono che il conteggio è 11 e nessuno dei due restituisce vero.

Quindi questo codice non è thread-safe.

In sostanza, tutti i problemi multi-threading sono causati da alcune variazioni di questo tipo di problema.

La soluzione è garantire che l'aggiunta e il confronto non possano essere separati (ad esempio circondando le due istruzioni con un qualche tipo di codice di sincronizzazione) o inventando una soluzione che non richiede due operazioni. Tale codice sarebbe sicuro per i thread.


1

Almeno in C ++, penso che thread-safe sia un po 'un termine improprio in quanto lascia molto fuori dal nome. Per essere thread-safe, il codice deve in genere essere proattivo al riguardo. Non è generalmente una qualità passiva.

Affinché una classe sia a prova di passo, deve avere caratteristiche "extra" che aggiungano sovraccarico. Queste funzionalità fanno parte dell'implementazione della classe e, in generale, nascoste dall'interfaccia. Cioè, thread diversi possono accedere a qualsiasi membro della classe senza mai preoccuparsi di entrare in conflitto con un accesso simultaneo da parte di un thread diverso E possono farlo in modo molto pigro, usando il semplice vecchio stile di codifica umana normale, senza doverlo fare tutta quella folle roba di sincronizzazione che è già stata introdotta nelle viscere del codice chiamato.

Ed è per questo che alcune persone preferiscono usare il termine sincronizzato internamente .

Set terminologici

Esistono tre serie principali di terminologia per queste idee che ho incontrato. Il primo e storicamente più popolare (ma peggio) è:

  1. filo sicuro
  2. non thread-safe

Il secondo (e migliore) è:

  1. a prova di filo
  2. thread compatibile
  3. thread ostile

Un terzo è:

  1. sincronizzato internamente
  2. sincronizzato esternamente
  3. unsynchronizeable

analogie

thread safe ~ thread thread ~ sincronizzato internamente

Un esempio di un sistema sincronizzato internamente (alias thread-safe o thread proof ) è un ristorante in cui un host ti saluta alla porta e ti impedisce di fare la fila. L'host fa parte del meccanismo del ristorante per trattare con più clienti e può usare alcuni trucchi piuttosto difficili per ottimizzare i posti a sedere dei clienti in attesa, come prendere in considerazione le dimensioni della loro festa o quanto tempo sembrano avere o addirittura prendere prenotazioni per telefono. Il ristorante è sincronizzato internamente perché tutto ciò fa parte dell'interfaccia per interagire con esso.

non thread-safe (ma piacevole) ~ thread compatibile ~ sincronizzato esternamente ~ a thread libero

Supponi di andare in banca. C'è una linea, cioè una contesa per i cassieri. Poiché non sei un selvaggio, riconosci che la cosa migliore da fare nel mezzo della contesa per una risorsa è fare la fila come un essere civile. Nessuno tecnicamente ti fa fare questo. Speriamo che tu abbia la programmazione sociale necessaria per farlo da solo. In questo senso, la lobby della banca è sincronizzata esternamente. Dovremmo dire che non è sicuro? questo è ciò che l'implicazione è che se si va con il thread-safe , filo-unsafe set terminologia bipolare. Non è un ottimo insieme di termini. La migliore terminologia è sincronizzata esternamente,La lobby della banca non è ostile all'accesso di più clienti, ma non fa nemmeno il lavoro di sincronizzarli. I clienti lo fanno da soli.

Questo è anche chiamato "free threaded", dove "free" è come in "free from pidocchi" - o in questo caso, i blocchi. Bene, più precisamente, primitive di sincronizzazione. Ciò non significa che il codice possa essere eseguito su più thread senza quelle primitive. Significa solo che non viene fornito con quelli già installati e dipende da te, l'utente del codice, installarli tu stesso come ritieni opportuno. Installare le tue primitive di sincronizzazione può essere difficile e richiede una riflessione approfondita sul codice, ma può anche portare al programma più veloce possibile consentendoti di personalizzare il modo in cui il programma viene eseguito sulle attuali CPU hyperthreaded.

non thread safe (e bad) ~ thread ostile ~ non sincronizzabile

Un'analogia quotidiana di esempio di un sistema thread-ostile è un cretino con un'auto sportiva che rifiuta di usare i paraocchi e cambia corsia volenti o nolenti. Il loro stile di guida è thread ostile o insicronizzabile perché non hai modo di coordinarti con loro, e questo può portare a contese per la stessa corsia, senza risoluzione, e quindi un incidente mentre due auto tentano di occupare lo stesso spazio, senza alcun protocollo per evitarlo. Questo modello può anche essere pensato più in generale come antisociale, che preferisco perché è meno specifico per i thread e quindi più generalmente applicabile a molte aree della programmazione.

Perché thread safe et al. sono un insieme terminologico errato

Il primo e il più vecchio set di terminologia non riesce a distinguere meglio tra ostilità e compatibilità dei thread . La compatibilità dei thread è più passiva della cosiddetta sicurezza dei thread, ma ciò non significa che il codice chiamato non sia sicuro per l'uso simultaneo dei thread. Significa solo che è passiva la sincronizzazione che lo consentirebbe, rimandandolo al codice chiamante, invece di fornirlo come parte della sua implementazione interna. Il thread compatibile è probabilmente il modo in cui il codice dovrebbe essere scritto per impostazione predefinita nella maggior parte dei casi, ma questo è purtroppo spesso considerato erroneamente come thread non sicuro, come se fosse intrinsecamente anti-sicurezza, che è un grande punto di confusione per i programmatori.

NOTA: molti manuali di software usano effettivamente il termine "thread-safe" per fare riferimento a "thread-compatibile", aggiungendo ancora più confusione a ciò che era già un disastro! Evito il termine "thread-safe" e "thread-unssafe" a tutti i costi proprio per questo motivo, poiché alcune fonti chiameranno qualcosa di "thread-safe" mentre altri lo chiameranno "thread-unssafe" perché non possono essere d'accordo se devi soddisfare alcuni standard extra per la sicurezza (primitive di sincronizzazione) o semplicemente NON essere ostile per essere considerato "sicuro". Quindi evita quei termini e usa invece i termini più intelligenti, per evitare pericolose comunicazioni errate con altri ingegneri.

Promemoria dei nostri obiettivi

In sostanza, il nostro obiettivo è quello di sovvertire il caos.

Lo facciamo creando sistemi deterministici su cui possiamo contare. Il determinismo è costoso, principalmente a causa dei costi opportunità derivanti dalla perdita di parallelismo, pipeline e riordino. Cerchiamo di ridurre al minimo la quantità di determinismo di cui abbiamo bisogno per mantenere bassi i nostri costi, evitando anche di prendere decisioni che possano erodere ulteriormente quel poco determinismo che possiamo permetterci.

La sincronizzazione dei thread riguarda l'aumento dell'ordine e la riduzione del caos. I livelli ai quali lo fai corrispondono ai termini sopra menzionati. Il livello più alto indica che un sistema si comporta sempre in modo del tutto prevedibile. Il secondo livello indica che il sistema si comporta abbastanza bene che il codice chiamante può rilevare in modo affidabile l'imprevedibilità. Ad esempio, un risveglio spurio in una variabile di condizione o un errore nel bloccare un mutex perché non è pronto. Il terzo livello indica che il sistema non si comporta abbastanza bene per giocare con chiunque altro e può ESSERE SEMPRE eseguito a thread singolo senza incorrere nel caos.


1

Invece di pensare al codice o alle classi come thread safe o no, penso che sia più utile pensare alle azioni come thread-safe. Due azioni sono thread-safe se si comporteranno come specificato quando eseguite da contesti di threading arbitrari. In molti casi, le classi supporteranno alcune combinazioni di azioni in modo thread-safe e altre no.

Ad esempio, molte raccolte come elenchi di array e set di hash garantiranno che se inizialmente si accede esclusivamente con un thread e non vengono mai modificate dopo che un riferimento diventa visibile ad altri thread, possono essere letti in modo arbitrario da qualsiasi combinazione di thread senza interferenze.

Ancora più interessante, alcune raccolte di set di hash come quella originale non generica in .NET, possono offrire una garanzia che fino a quando non viene mai rimosso alcun elemento, e a condizione che solo un thread li scriva, qualsiasi thread che tenta di leggere la raccolta si comporterà come se accedesse a una raccolta in cui gli aggiornamenti potrebbero essere ritardati e avvenire in ordine arbitrario, ma che altrimenti si comporteranno normalmente. Se il thread n. 1 aggiunge X e poi Y, e il thread n. 2 cerca e vede Y e quindi X, sarebbe possibile per il thread n. 2 vedere che Y esiste ma X no; se tale comportamento sia "thread-safe" dipende dal fatto che il thread n. 2 sia pronto a gestire tale possibilità.

Come nota finale, alcune classi - in particolare il blocco delle librerie di comunicazione - possono avere un metodo "close" o "Dispose" che è thread-safe rispetto a tutti gli altri metodi, ma nessun altro metodo thread-safe rispetto a l'un l'altro. Se un thread esegue una richiesta di lettura bloccante e un utente del programma fa clic su "annulla", non ci sarebbe modo per una richiesta di chiusura di essere emessa dal thread che sta tentando di eseguire la lettura. La richiesta di chiusura / eliminazione, tuttavia, può impostare in modo asincrono un flag che provocherà la cancellazione della richiesta di lettura il più presto possibile. Una volta eseguita la chiusura su qualsiasi thread, l'oggetto diventerebbe inutile e tutti i tentativi di azioni future fallirebbero immediatamente,


0

In parole più semplici: P Se è sicuro eseguire più thread su un blocco di codice, è thread-safe *

* si applicano le condizioni

Le condizioni sono menzionate da altre risposte come 1. Il risultato dovrebbe essere lo stesso se si esegue un thread o più thread su di esso, ecc.

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.