Domanda

Ho un C# classe singleton che più classi.L'accesso è attraverso Instance per il Toggle() metodo thread-safe?Se sì, con quali presupposti, regole, etc.Se no, perché e come posso risolvere il problema?

public class MyClass
{
    private static readonly MyClass instance = new MyClass();

    public static MyClass Instance
    {
        get { return instance; }
    }

    private int value = 0;

    public int Toggle()
    {
        if(value == 0) 
        {
            value = 1; 
        }
        else if(value == 1) 
        { 
            value = 0; 
        }

        return value;
    }
}
È stato utile?

Soluzione

L'accesso è attraverso un 'Esempio' di 'Toggle()' class threadsafe?Se sì, con quali presupposti, regole, etc.Se no, perché e come posso risolvere il problema?

No, non è threadsafe.

In sostanza, entrambi i thread possono eseguire il Toggle funzione allo stesso tempo, in modo che questo possa accadere

    // thread 1 is running this code
    if(value == 0) 
    {
        value = 1; 
        // RIGHT NOW, thread 2 steps in.
        // It sees value as 1, so runs the other branch, and changes it to 0
        // This causes your method to return 0 even though you actually want 1
    }
    else if(value == 1) 
    { 
        value = 0; 
    }
    return value;

È necessario operare con le seguenti ipotesi.

Se 2 thread sono in esecuzione, essi possono e si interleave e interagire con loro in modo casuale in qualsiasi punto.Si può essere a metà strada attraverso la scrittura o la lettura di un numero intero a 64 bit o a virgola mobile (a 32 bit CPU) e un altro thread possono accedere e modificare fuori da sotto di voi.

Se i 2 thread non accedere mai a nulla in comune, non importa, ma non appena lo fanno, è necessario per impedire loro di farsi su ogni altri piedi.Il modo per farlo in .NET è con le serrature.

Si può decidere cosa e dove lock, pensando a cose come questa:

Per un determinato blocco di codice, se il valore di something ha cambiato fuori da sotto di me, è importante?Se si, è necessario per bloccare la something per tutta la durata del codice in cui sarebbe un problema.

Guardando il tuo esempio di nuovo

    // we read value here
    if(value == 0) 
    {
        value = 1; 
    }
    else if(value == 1) 
    { 
        value = 0; 
    }
    // and we return it here
    return value;

Per tornare quello che ci aspettiamo, si assume che value non avere cambiato tra la lettura e la return.In ordine per questa ipotesi, di essere effettivamente corretta, è necessario bloccare value per la durata del blocco di codice.

Così si potrebbe fare questo:

lock( value )
{
     if(value == 0) 
     ... // all your code here
     return value;
}

TUTTAVIA

In .NET è possibile bloccare i Tipi di Riferimento.Int32 è un Tipo di Valore, quindi non siamo in grado di bloccarlo.
Siamo in grado di risolvere questo con l'introduzione di un 'dummy' oggetto, e di chiusura che ovunque noi vogliamo bloccare 'valore'.

Questo è ciò che Ben Scheirman fa riferimento.

Altri suggerimenti

Originale impplementation non è thread-safe, come Ben sottolinea

Un modo semplice per rendere thread-safe è quello di introdurre un blocco economico.Es.come questa:

public class MyClass
{
    private Object thisLock = new Object();
    private static readonly MyClass instance = new MyClass();
    public static MyClass Instance
    {
        get { return instance; }
    }
    private Int32 value = 0;
    public Int32 Toggle()
    {
        lock(thisLock)
        {
            if(value == 0) 
            {
                value = 1; 
            }
            else if(value == 1) 
            { 
                value = 0; 
            }
            return value;
        }
    }
}

Che è quello che ho pensato.Ma, io sto alla ricerca di dettagli...Toggle()' non è un metodo statico, ma è un membro di una proprietà statica (quando l'utilizzo di 'Esempio').È che quello che fa è condivisa tra i thread?

Se la vostra applicazione è multi-threaded e si può prevedere che più thread di accedere a tale metodo, che rende condivisa tra i thread.Poiché la classe è un Singleton si sa che diversi thread di accedere all'oggetto STESSO, in modo da essere avvertiti circa la sicurezza di thread di metodi.

E come questo si applica a singleton in generale.Devo indirizzo questo in ogni metodo sulla mia classe?

Come ho detto sopra, perché è un singleton sapete vari thread acess lo stesso oggetto, possibilmente allo stesso tempo.Questo non significa che si deve fare ogni metodo di ottenere un blocco.Se si nota che un simultaneos invocazione può portare a stato danneggiato della classe, allora si dovrebbe applicare il metodo citato da @Thomas

Posso supporre che il singleton pattern espone mio altrimenti bella thread-safe di classe a tutti i thread di problemi di regolare i membri statici?

No.La tua classe è semplicemente non threadsafe.Singleton non ha nulla a che fare con esso.

(Io ricevo la mia testa intorno al fatto che i membri di istanza chiamato su un oggetto statico causare problemi di threading)

Non ha nulla a che fare con che.

Devi pensare come questo:È possibile che nel mio programma per 2 (o più) thread per accedere a questo pezzo di dati, allo stesso tempo?

Il fatto che è possibile ottenere i dati tramite un singleton, o variabile statica, o passando in un oggetto come parametro di un metodo non importa.Alla fine della giornata, è solo qualche bit e byte nel tuo PC RAM, e tutto ciò che conta è se più thread possono vedere gli stessi bit.

Il thread potrebbe fermarsi nel mezzo di questo metodo e di controllo del trasferimento di un thread diverso.Hai bisogno di una sezione critica di tutto il codice...

private static object _lockDummy = new object();


...

lock(_lockDummy)
{
   //do stuff
}

Vorrei anche aggiungere un costruttore protetto per MyClass per evitare che il compilatore di generare un costruttore predefinito pubblico.

Stavo pensando che se I dump il singleton pattern e forza a tutti di avere una nuova istanza della classe sarebbe facilità alcuni problemi...ma che non si ferma a chiunque altro di inizializzazione di un oggetto statico di che tipo e passare il tutto...o da spinning off più thread, accedendo a " Toggle()' dalla stessa istanza.

Bingo :-)

Ora ho capito.E ' un mondo difficile.Vorrei non erano refactoring del codice legacy :(

Purtroppo, il multithreading è difficile e devi essere molto paranoico di cose :-) La soluzione più semplice in questo caso è di attaccare con il singleton, e aggiungere un blocco di tutto il valore, come negli esempi.

Beh, io in realtà non conosco C# che bene...ma io sono ok in Java, così io vi darò la risposta per questo, e speriamo che i due sono abbastanza simili che sarà utile.Se non è così, mi scuso.

La risposta è no, non è sicuro.Un thread può chiamare Toggle (), al tempo stesso, come gli altri, ed è possibile, anche se improbabile, con questo codice, che Thread1 può impostare value tra le volte che Thread2 verifica quando si imposta.

Per risolvere, basta fare Toggle() synchronized.Non blocco nulla o chiamare qualsiasi cosa che potrebbe generare un altro thread che si potrebbe chiamare Toggle(), in modo che tutto quello che dovete fare salvarlo.

Citazione:

if(value == 0) { value = 1; }
if(value == 1) { value = 0; }
return value;

value sarà sempre 0...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top