Vorsicht bei String als Lock-Objekt

Wenn man denn unbedingt einen String als Lock-Objekt nutzen will und man sich davon überzeugt hat, nicht den Type eines gerade durch die Methode schwirrenden Objektes zu nutzen (das könnte ja jeder machen ;-)), dann sollte man auf jeden Fall an String.Intern(myLockString) denken!

Wo man kann, sollte man also weiterhin einen einfachen object-Lock nutzen:

   1: private static readonly object _lock = new object();

Aber man darf dann nicht vergessen, dass alle Threads, die auf den Code im Lock zugreifen von einem einzigen Lock bedient werden. Wenn man die Threads aber in Gruppen aufteilen kann, die unabhängig voneinander arbeiten können, ist diese Beschränkung zu scharf. Ich suchte also einen Weg, einen eindeutigen Lock für jede dieser Gruppen (hier: TranslationGroup) zu generieren, der von allen Instanzen geteilt wird. Der Gruppenidentifikator heißt hier _pageNameKey.

Beispiel:

   1: private const string _migrateLock = "9B90360AAF4342798A739DA2D85AF5AF"; // guid
   2:  
   3: private string _pageNameKey = ...; // can be the same in multiple parallel threads
   4:  
   5: public void Migrate(string oldGroupName, Dictionary<string, string> controlIdsOldToNew)
   6: {
   7:     lock (string.Intern(_migrateLock + _pageNameKey))
   8:     {
   9:         // do something not here that should not be done in parallel
  10:     }
  11: }

Ließe man in Zeile 7 das String.Intern(…) weg, würde bei jedem Aufruf der Methode ein neues String-Objekt generiert – was für dem Lock seine Existenzberechtigung raubt.

Gruß, Oliver

Technorati-Tags: ,,,
Wenn ihnen der Artikel gefallen hat oder er für sie hilfreich war, bitten "kicken" sie ihn.
kick it on dotnet-kicks.de

Kommentare

März 28. 2010 09:35

Robert

Zu "String.Intern", ich dachte immer der lock mit einem String würde sich tatsächlich auf die Zeichenkette beziehen?

In der MSDN heist es:  msdn.microsoft.com/.../c5kehkcz(VS.80).aspx

"lock(“myLock”) is a problem since any other code in the process using the same string, will share the same lock."

Robert

März 28. 2010 09:39

Stefan

> ich dachte immer der lock mit einem String würde sich tatsächlich auf die Zeichenkette beziehen?

nur bei konstanten strings, welche ja automatisch interned werden. wenn du jedoch objekte uebergibst muss es das selbe sein.

> "lock(“myLock”) is a problem since any other code in the process using the same string, will share the same lock."

Deswegen ja die GUID Wink


gruss.
stefan

Stefan

März 29. 2010 08:43

Robert

Stefan, das macht noch nicht so recht viel Sinn, da bei lock(“myLockString”) "myLockString" eine lokale Variable ist, also weder eine Konstante noch "interned".

In der MSDN heißt es: "Die Common Language Runtime behält den Speicherplatz von Zeichenfolgen durch Verwalten einer Tabelle bei, die als Internpool bezeichnet wird und einen Verweis auf jedes eindeutige Zeichenfolgenliteral enthält, das im Programm deklariert oder programmgesteuert erstellt wurde. Daher ist eine Instanz eines Zeichenfolgenliterals mit einem bestimmten Wert nur einmal im System vorhanden."

Soweit ich das also vestehe ist natürlich bei string locks das Problem vorhanden, dass der selbe string von einem anderen lock zufällig genutzt wird. Unabhängig  davon, würde der lock auch ohne "string.intern" funktionieren.

(Für quick und dirty habe ich mir angewöhnt locks so zu definieren: lock("namespace.type.funktion.evtl.beschreibung").

Ein Problem der String locks ist auch, dass sie bei "CompilationRelaxations.NoStringInterning" (msdn.microsoft.com/.../....nostringinterning.aspx) nicht mehr funktionieren würden, wobei ich noch nie gehört/gesehen haben, dass das jemand verwendet.

Robert

März 29. 2010 08:58

Stefan

> Stefan, das macht noch nicht so recht viel Sinn, da bei lock(“myLockString”) "myLockString" eine lokale Variable ist, also weder eine Konstante noch "interned".

Zur compile-zeit bekannte strings werden interned.

> In der MSDN heißt es: "Die Common Language Runtime behält den Speicherplatz von Zeichenfolgen durch Verwalten einer Tabelle bei, die als Internpool bezeichnet wird und einen Verweis auf jedes eindeutige Zeichenfolgenliteral enthält, das im Programm deklariert oder programmgesteuert erstellt wurde. Daher ist eine Instanz eines Zeichenfolgenliterals mit einem bestimmten Wert nur einmal im System vorhanden."

dies gilt nur fuer "Zeichenfolgenliterale", dies sind zur compile-zeit bekannte, im code angegebene zeichenketten. zur laufzeit gebaute strings sind keine literale.

> Soweit ich das also vestehe ist natürlich bei string locks das Problem vorhanden, dass der selbe string von einem anderen lock zufällig genutzt wird. Unabhängig  davon, würde der lock auch ohne "string.intern" funktionieren.

nein, tut er nicht.

> Ein Problem der String locks ist auch, dass sie bei "CompilationRelaxations.NoStringInterning" (msdn.microsoft.com/.../....nostringinterning.aspx) nicht mehr funktionieren würden, wobei ich noch nie gehört/gesehen haben, dass das jemand verwendet.

dies schaltet nur interning zur compile-zeit ab. daher ja auch "compiling"Relaxations. Aufrufe auf String.Intern() bleiben davon unberuehrt.


gruss,
stefan

Stefan

März 29. 2010 09:04

Robert

Im Beispiel: lock("namespace.type.function"){} ist der string zur compile Zeit bekannt und wird also funktionieren

Robert

März 29. 2010 09:05

Stefan


> Im Beispiel: lock("namespace.type.function"){} ist der lock zur compile Zeit bekannt und wird also funktionieren

ja, das natuerlich. aber was oliver ja macht ist ein lock pro _pageNameKey:

lock (string.Intern(_migrateLock + _pageNameKey))

da werden 2 strings zusammengebaut, daher darf man das internen nicht vergessen.


gruss, stefan

Stefan

Kommentar schreiben


(Zeigt dein Gravatar icon)

  Country flag

biuquote
  • Kommentar
  • Live Vorschau
Loading



Über speak-friend

Wir Stefan, Oliver, Anton und Robert, sind die Entwickler von speak-friend.

Hier schreiben wir über unsere Coding-Abenteuer.

Letzten Kommentare