Cosa rende sicuro un metodo? Quali sono le regole?


156

Esistono regole / linee guida generali per ciò che rende un metodo thread-safe? Capisco che ci sono probabilmente un milione di situazioni una tantum, ma che dire in generale? È così semplice?

  1. Se un metodo accede solo alle variabili locali, è thread-safe.

È così? Questo vale anche per i metodi statici?

Una risposta, fornita da @Cybis, è stata:

Le variabili locali non possono essere condivise tra i thread poiché ogni thread ottiene il proprio stack.

È il caso anche dei metodi statici?

Se un metodo viene passato a un oggetto di riferimento, ciò rompe la sicurezza del thread? Ho fatto alcune ricerche e ci sono molte cose su alcuni casi, ma speravo di essere in grado di definire, usando solo alcune regole, le linee guida da seguire per assicurarsi che un metodo sia sicuro per i thread.

Quindi, immagino che la mia ultima domanda sia: "Esiste un breve elenco di regole che definiscono un metodo thread-safe? In caso affermativo, quali sono?"

MODIFICA
Molti punti positivi sono stati fatti qui. Penso che la vera risposta a questa domanda sia: "Non ci sono regole semplici per garantire la sicurezza del thread". Freddo. Belle. Ma in generale penso che la risposta accettata fornisca un riassunto buono e breve. Ci sono sempre delle eccezioni. Così sia. Posso vivere con quello.


59
Non accederai alle variabili a cui accedono anche altri thread senza un locketh.
Hans Passant,

4
Hanth Pathant si è trasformato in un vigore!
Martin James,

3
Inoltre .. "Non accederai alle variabili che sono accessibili anche da altri thread senza un locketh" - fino a che non importa che il valore letto sia occasionalmente non l'ultimo o sia in realtà gravemente errato.
Martin James,

Ecco un bel blog di Eric che ti porterà in un turbine.
RBT,

Risposte:


140

Se un metodo (istanza o statico) fa riferimento solo a variabili nell'ambito di tale metodo, è sicuro per i thread poiché ogni thread ha il proprio stack:

In questo caso, più thread potrebbero chiamare ThreadSafeMethodcontemporaneamente senza problemi.

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number; // each thread will have its own variable for number.
        number = parameter1.Length;
        return number;
    }
}

Questo vale anche se il metodo chiama un altro metodo di classe che fa riferimento solo a variabili con ambito locale:

public class Thing
{
    public int ThreadSafeMethod(string parameter1)
    {
        int number;
        number = this.GetLength(parameter1);
        return number;
    }

    private int GetLength(string value)
    {
        int length = value.Length;
        return length;
    }
}

Se un metodo accede a proprietà o campi (stato oggetto) (istanza o statico), è necessario utilizzare i blocchi per assicurarsi che i valori non vengano modificati da un thread diverso.

public class Thing
{
    private string someValue; // all threads will read and write to this same field value

    public int NonThreadSafeMethod(string parameter1)
    {
        this.someValue = parameter1;

        int number;

        // Since access to someValue is not synchronised by the class, a separate thread
        // could have changed its value between this thread setting its value at the start 
        // of the method and this line reading its value.
        number = this.someValue.Length;
        return number;
    }
}

È necessario essere consapevoli del fatto che eventuali parametri passati al metodo che non sono né una struttura né immutabili potrebbero essere mutati da un altro thread al di fuori dell'ambito del metodo.

Per garantire la corretta concorrenza è necessario utilizzare il blocco.

per ulteriori informazioni consultare l' istruzione lock C # reference e ReadWriterLockSlim .

lock è principalmente utile per fornire una funzionalità alla volta,
ReadWriterLockSlimè utile se hai bisogno di più lettori e singoli autori.


15
Nel terzo esempio private string someValue;non è staticcosì ogni istanza otterrà una copia separata di quella variabile. Quindi, per favore, puoi spiegare come questo non è thread-safe?
Bharadwaj,

29
@Bharadwaj se esiste un'istanza della Thingclasse a cui accedono più thread
Trevor Pilley,

112

Se un metodo accede solo alle variabili locali, è thread-safe. È così?

Assolutamente no. È possibile scrivere un programma con una sola variabile locale a cui si accede da un singolo thread che tuttavia non è thread-safe:

https://stackoverflow.com/a/8883117/88656

Questo vale anche per i metodi statici?

Assolutamente no.

Una risposta, fornita da @Cybis, è stata: "Le variabili locali non possono essere condivise tra i thread perché ogni thread ottiene il proprio stack".

Assolutamente no. La caratteristica distintiva di una variabile locale è che è visibile solo dall'ambito locale , non che è allocata nel pool temporaneo . È perfettamente legale e possibile accedere alla stessa variabile locale da due thread diversi. Puoi farlo usando metodi anonimi, lambda, blocchi iteratori o metodi asincroni.

È il caso anche dei metodi statici?

Assolutamente no.

Se un metodo viene passato a un oggetto di riferimento, ciò rompe la sicurezza del thread?

Può essere.

Ho fatto alcune ricerche e ci sono molte cose su alcuni casi, ma speravo di essere in grado di definire, usando solo alcune regole, le linee guida da seguire per assicurarsi che un metodo sia sicuro per i thread.

Dovrai imparare a vivere con delusione. Questo è un argomento molto difficile.

Quindi, immagino che la mia ultima domanda sia: "Esiste un breve elenco di regole che definiscono un metodo thread-safe?

No. Come hai visto dal mio esempio in precedenza, un metodo vuoto può essere non thread-safe . Potresti anche chiedere "c'è un breve elenco di regole che garantisce che un metodo sia corretto ". No non c'è. La sicurezza del filo non è altro che un tipo estremamente complicato di correttezza.

Inoltre, il fatto che tu stia ponendo la domanda indica il tuo fondamentale fraintendimento sulla sicurezza del thread. La sicurezza del thread è una proprietà globale , non locale di un programma. Il motivo per cui è così difficile ottenere il giusto risultato è che devi avere una conoscenza completa del comportamento di threading dell'intero programma al fine di garantirne la sicurezza.

Ancora una volta, guarda il mio esempio: ogni metodo è banale . È il modo in cui i metodi interagiscono tra loro a livello "globale" che rende il deadlock del programma. Non puoi guardare ogni metodo e selezionarlo come "sicuro" e quindi aspettarti che l'intero programma sia sicuro, non più di quanto tu possa concludere che poiché la tua casa è fatta di mattoni al 100% non vuoti che la casa è anche non cavo. La cavità di una casa è una proprietà globale dell'intera cosa, non un aggregato delle proprietà delle sue parti.


15
Dichiarazione fondamentale: la sicurezza dei thread è una proprietà globale, non locale di un programma.
Greg D,

5
@BobHorn: class C { public static Func<int> getter; public static Action<int> setter; public static void M() { int x = 0; getter = ()=>x; setter = y=>{x=y;};} } chiama M (), quindi chiama C.getter e C.setter su due thread diversi. Ora la variabile locale può essere scritta e letta su due thread diversi anche se è locale. Ancora una volta: la caratteristica che definisce una variabile locale è che è locale , non che si trova nello stack del thread .
Eric Lippert,

3
@BobHorn: penso che abbia più a che fare con la comprensione dei nostri strumenti a un livello tale da poterne parlare con autorità e conoscenza. Ad esempio, la domanda originale ha tradito una mancanza di comprensione su cosa sia una variabile locale. Questo errore è abbastanza comune, ma il fatto è che è un errore e deve essere corretto. La definizione di una variabile locale è abbastanza precisa e dovrebbe essere trattata di conseguenza. :) Questa non è una risposta per "persone" che non capiscono l'esistenza di casi patologici. Questi sono un chiarimento in modo che tu possa capire cosa hai effettivamente chiesto. :)
Greg D

7
@EricLippert: la documentazione MSDN per molte classi dichiara che i membri statici pubblici (condivisi in Visual Basic) di questo tipo sono thread-safe. Non è garantito che tutti i membri dell'istanza siano thread-safe ". o talvolta "Questo tipo è thread-safe". (ad es. String), come possono essere fornite queste garanzie quando la sicurezza dei thread è una preoccupazione globale?
Mike Zboray,

3
@mikez: bella domanda. Questi metodi non mutano alcuno stato o, quando lo fanno, mutano lo stato in modo sicuro senza blocchi, oppure, se usano i blocchi, lo fanno in modo tale da garantire che l'ordine globale dei blocchi non venga violato. Le stringhe sono strutture di dati immutabili i cui metodi non mutano nulla, quindi le stringhe possono essere facilmente rese sicure.
Eric Lippert,

12

Non c'è una regola dura e veloce.

Ecco alcune regole per rendere sicuro il thread di codice in .NET e perché queste non sono buone regole:

  1. La funzione e tutte le funzioni che chiama devono essere pure (senza effetti collaterali) e utilizzare variabili locali. Anche se questo renderà il tuo thread sicuro per il codice, ci sono anche pochissime cose interessanti che puoi fare con questa limitazione in .NET.
  2. Ogni funzione che opera su un oggetto comune deve avere lockuna cosa comune. Tutti i blocchi devono essere eseguiti nello stesso ordine. Ciò renderà sicuro il thread del codice, ma sarà incredibilmente lento e potresti anche non utilizzare più thread.
  3. ...

Non esiste una regola che renda sicuro il thread di codice, l'unica cosa che puoi fare è assicurarti che il tuo codice funzionerà indipendentemente da quante volte viene eseguito attivamente, ogni thread può essere interrotto in qualsiasi momento, con ogni thread in il proprio stato / posizione e questo per ogni funzione (statica o di altro tipo) che accede a oggetti comuni.


10
Le serrature non devono essere lente. Le serrature sono incredibilmente veloci; una serratura non contestata è nell'ordine di magnitudo da dieci a cento nanosecondi . I blocchi contestati sono ovviamente arbitrariamente lenti; se si è rallentati perché si stanno contestando i blocchi, quindi ricollegare il programma per rimuovere la contesa .
Eric Lippert,

2
Immagino di non aver chiarito abbastanza bene la seconda posizione. Stavo cercando di dire che esiste un modo per rendere sicuri i thread: mettere i blocchi attorno a ogni accesso di qualsiasi oggetto comune. E supponendo che ci siano più thread, genererà contesa e i blocchi rallenteranno il tutto. Quando si aggiungono blocchi, non si può semplicemente aggiungere ciechi blocchi, devono essere collocati in ottimi punti strategici.
EarlNameless

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.