2010-11-16 13 views
18

Mentre osservavo un codice di applicazione legacy, ho notato che utilizza un oggetto stringa per eseguire la sincronizzazione dei thread. Sto cercando di risolvere alcuni problemi di conflitto di thread in questo programma e mi chiedevo se questo potesse portare a situazioni così strane. Qualche idea ?Utilizzo della stringa come blocco per eseguire la sincronizzazione dei thread

private static string mutex= "ABC"; 

internal static void Foo(Rpc rpc) 
{ 
    lock (mutex) 
    { 
     //do something 
    } 
} 

risposta

32

stringhe del genere (dal codice) potrebbe essere "interned". Ciò significa che tutte le istanze di "ABC" puntano allo stesso oggetto. Anche attraverso AppDomain s puoi puntare allo stesso oggetto (grazie Steven per la punta).

Se si hanno molti mutex di stringa, da posizioni diverse, ma con lo stesso testo, tutti possono bloccare lo stesso oggetto.

Il pool interno conserva la memorizzazione di stringhe. Se si assegna una costante di stringa letterale a più variabili, ciascuna variabile viene impostata per fare riferimento alla stessa costante nel pool interno anziché fare riferimento a diverse istanze di String con valori identici.

E 'meglio usare:

private static readonly object mutex = new object(); 

Inoltre, dal momento che la stringa non è const o readonly, si può cambiare. Quindi (in teoria) è possibile bloccare il mutex. Cambia mutex in un altro riferimento, quindi inserisci una sezione critica perché il blocco utilizza un altro oggetto/riferimento. Esempio:

private static string mutex = "1"; 
private static string mutex2 = "1"; // for 'lock' mutex2 and mutex are the same 

private static void CriticalButFlawedMethod() { 
    lock(mutex) { 
     mutex += "."; // Hey, now mutex points to another reference/object 
     // You are free to re-enter 
     ... 
    } 
} 
+0

Grazie GvS. Ma mi chiedevo se c'è una differenza nell'usare un tipo di oggetto (che generalmente facciamo) per bloccare l'uso di un tipo di stringa (la stringa essendo mutevole). il programma sta avendo un gran numero di discussioni sul thread (non è ancora sicuro se questo è dovuto a questo pezzo di codice). – Illuminati

+0

Le stringhe non sono mutabili, sembrano semplicemente mutabili, ma ogni stringa diversa punta a un altro riferimento. Mi sembra strano bloccarli. Hai provato un ReaderWriterLock. – GvS

+0

scusa volevo dire "string essere immutabile" :) ..non ho provato con ReaderWriterLock .. stavo solo cercando di capire la sezione esatta del codice che causa i problemi. è difficile ricreare la situcazione nell'ambiente locale a causa di dati insufficienti. ma ho intenzione di provare a riprodurlo. grazie ancora. – Illuminati

1

Se è necessario bloccare una stringa, è possibile creare un oggetto che le coppie la stringa con un oggetto che è possibile bloccare con.

class LockableString 
{ 
    public string _String; 
    public object MyLock; //Provide a lock to the data in. 

    public LockableString() 
    { 
      MyLock = new object(); 
    } 
} 
14

Per rispondere alla tua domanda (come alcuni altri hanno già fatto), ci sono alcuni potenziali problemi con l'esempio di codice che hai fornito:

private static string mutex= "ABC"; 
  • La variabile mutex non è immutabile.
  • La stringa letterale "ABC" farà riferimento allo stesso riferimento all'oggetto internamente in qualsiasi punto dell'applicazione.

In generale, vorrei sconsigliare il blocco delle stringhe. Tuttavia, c'è un caso in cui mi sono imbattuto dove è utile farlo.

Ci sono state occasioni in cui ho mantenuto un dizionario di oggetti di blocco in cui la chiave è qualcosa di unico su alcuni dati che ho. Ecco un esempio inventato:

void Main() 
{ 
    var a = new SomeEntity{ Id = 1 }; 
    var b = new SomeEntity{ Id = 2 }; 

    Task.Run(() => DoSomething(a));  
    Task.Run(() => DoSomething(a));  
    Task.Run(() => DoSomething(b));  
    Task.Run(() => DoSomething(b)); 
} 

ConcurrentDictionary<int, object> _locks = new ConcurrentDictionary<int, object>();  
void DoSomething(SomeEntity entity) 
{ 
    var mutex = _locks.GetOrAdd(entity.Id, id => new object()); 

    lock(mutex) 
    { 
     Console.WriteLine("Inside {0}", entity.Id); 
     // do some work 
    } 
} 

L'obiettivo di codice come questo è quello di serializzare invocazioni concorrenti del DoSomething() nel contesto dell'entità del Id. Il rovescio della medaglia è il dizionario. Più sono le entità, più diventa grande. È anche solo più codice da leggere e pensare.

Penso.internato stringa di NET può semplificare le cose:

void Main() 
{ 
    var a = new SomeEntity{ Id = 1 }; 
    var b = new SomeEntity{ Id = 2 }; 

    Task.Run(() => DoSomething(a));  
    Task.Run(() => DoSomething(a));  
    Task.Run(() => DoSomething(b));  
    Task.Run(() => DoSomething(b)); 
} 

void DoSomething(SomeEntity entity) 
{ 
    lock(string.Intern("dee9e550-50b5-41ae-af70-f03797ff2a5d:" + entity.Id)) 
    { 
     Console.WriteLine("Inside {0}", entity.Id); 
     // do some work 
    } 
} 

La differenza qui è che sto facendo leva sulla stringa di internato a darmi lo stesso riferimento a un oggetto per l'ID entità. Questo semplifica il mio codice perché non devo mantenere il dizionario delle istanze mutex.

Notare la stringa UUID codificata che sto utilizzando come spazio dei nomi. Questo è importante se scelgo di adottare lo stesso approccio di blocco su stringhe in un'altra area della mia applicazione.

Il blocco delle stringhe può essere una buona idea o una cattiva idea a seconda delle circostanze e dell'attenzione che lo sviluppatore fornisce ai dettagli.

+0

Questa risposta era in realtà ciò che stavo cercando. Ho bisogno di una garanzia che la stessa stringa crei lo stesso oggetto mutex e che l'internamento di una stringa sia un meccanismo molto preciso da usare per quello. Ne ho bisogno per scopi di memorizzazione nella cache: più thread possono recuperare lo stesso oggetto dal DB e devono inserire solo 1 voce nella cache THE, che ha una chiave di "Id". Quindi ora costruisco solo una stringa per il blocco da questo ID e una stringa arbitraria che non verrà mai utilizzata da nessuna parte (qualcosa come Guid per coloro che vogliono essere relativamente sicuri) e eseguirà l'istanziazione degli oggetti e l'aggiornamento della cache in questo blocco. – evilkos

+0

Sono contento che tu abbia trovato utile questo approccio! –

+0

Il problema con il dizionario infinitamente growinh può essere risolto usando 'ConditionalWeakTable'. Questo è un dizionario che consente a entrambe le chiavi come valori di andare fuori dal campo di applicazione, il che consente di raccogliere informazioni su immondizia – Steven

Problemi correlati