Utilizzare la stringa come blocco per eseguire la sincronizzazione dei thread

Mentre stavo guardando un certo codice di applicazione legacy ho notato che sta usando un object 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 ?

private static string mutex= "ABC"; internal static void Foo(Rpc rpc) { lock (mutex) { //do something } } 

Stringhe come quella (dal codice) potrebbero essere ” internate “. Ciò significa che tutte le istanze di “ABC” puntano allo stesso object. Anche attraverso AppDomain puoi puntare allo stesso object (grazie Steven per la punta).

Se disponi di molti mutex per le stringhe, da posizioni diverse, ma con lo stesso testo, potrebbero bloccare tutti lo stesso object.

Il pool interno conserva l’archiviazione 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.

È meglio usare:

  private static readonly object mutex = new object(); 

Inoltre, poiché la tua stringa non è const o readonly , puoi cambiarla. Quindi (in teoria) è ansible bloccare il mutex. Cambia mutex in un altro riferimento, quindi inserisci una sezione critica perché il blocco utilizza un altro object / 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 ... } } 

Per rispondere alla tua domanda (come altri hanno già), 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’object internamente in qualsiasi punto dell’applicazione.

In generale, consiglierei di non bloccare le 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 forzato:

 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 _locks = new ConcurrentDictionary(); 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’objective del codice come questo è serializzare le chiamate simultanee di DoSomething() nel contesto Id dell’entity framework. Il rovescio della medaglia è il dizionario. Più sono le quadro, più diventa grande. È anche solo più codice da leggere e pensare.

Penso che l’interning delle stringhe di .NET possa 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 affidamento sull’internamento delle stringhe per darmi lo stesso riferimento all’object per ID quadro. Questo semplifica il mio codice perché non devo mantenere il dizionario delle istanze mutex.

Si noti la stringa UUID hard-coded 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.

Bloccare le stringhe può essere una buona idea o una ctriggers idea a seconda delle circostanze e dell’attenzione che lo sviluppatore fornisce ai dettagli.

Se è necessario bloccare una stringa, è ansible creare un object che accoppi la stringa con un object che è ansible bloccare con.

 class LockableString { public string _String; public object MyLock; //Provide a lock to the data in. public LockableString() { MyLock = new object(); } }