Was ich realisieren wollte war ein Locking im Backend. Mehrere Anwendungen holen sich Datensätze mit einer Meldungsnummer und diese Kombination soll eben nur 1x exklusiv abgeholt werden.
Ich habe dazu folgendes Konstrukt gebaut:
private class EVT_Standardeventhandler_Lock
{
public int Meldungsnummer { get; set; }
public DateTime Lockdate { get; set; }
}
public class EVT_Standardeventhandler_Locks
{
List<EVT_Standardeventhandler_Lock> lstLocks = new List<EVT_Standardeventhandler_Lock>();
/// <summary>
/// Gibt True zurück wenn Lock erfolgreich gesetzt wurde, sonst False
/// </summary>
public bool Lock(int iMeldungsnummer)
{
DateTime datNow = DateTime.Now;
lstLocks.RemoveAll(x => x.Lockdate.AddMinutes(2) < datNow); //alle Locks älter als 2 Minuten sind Schrott -> freigeben
if (lstLocks.Any(x => x.Meldungsnummer == iMeldungsnummer))
return false;
lstLocks.Add(new EVT_Standardeventhandler_Lock() { Lockdate = datNow, Meldungsnummer = iMeldungsnummer });
return true;
}
public void Unlock(int iMeldungsnummer)
{
lstLocks.RemoveAll(x => x.Meldungsnummer == iMeldungsnummer);
}
}
EVT_Standardeventhandler_Locks befindet sich im Sessionstate und von Zeit zu Zeit bekomme ich bei der Any()-Prüfung eine
System.InvalidOperationException: Die Auflistung wurde geändert. Der Enumerationsvorgang kann möglicherweise nicht ausgeführt werden.
Ich schon mal ein wenig Google zum Thema befragt und es scheinen konkurrierende Threads der Auslöser zu sein. Anscheinend ist Any hier nicht threadsafe.
Tja die Frage ist jetzt, ob das irgendwie schöner mit Boardmitteln (.NET 3.5) geht, oder ob ich hier tatsächlich alles auf for-Schleifen-Handling
for (int i = x.Count - 1; i >= 0; i--)
umbauen muss.
Grüße,
Chris
"Wenn Architekten genauso bauen würden, wie Programmierer programmieren, dann würde der erste Specht, der vorbeikommt, die Zivilisation zerstören." (Steven Weinberg)
Deine Collection lstLocks
ist nicht Thread-safe bzw List<T> an für sich ist von Haus aus nicht Thread-Safe; Any() ist hierbei egal. Auch for hilft hier nicht - der Fehler würde genauso auftreten.
Entweder lock() oder geeignete Collections wie ConcurrentBag verwenden.
Dahingehen musst Du die Klasse EVT_Standardeventhandler_Lock ebenfalls Thread-Safe umsetzen.
Grundlagen zu diesem Thema: [Artikel] Multi-Threaded Programmierung
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Mhh soweit ich das überblicke beschäftigt sich der Artikel hauptsächlich mit mehreren Thread, die man auch unter Kontrolle hat. In meinem Fall rufen mehrere Instanzen einer Anwendung jeweils eine Webservicemethode auf und unter der Haube ist dann im Backend die im Sessionstate gehaltene Klasse.
Oder übersehe ich was?
Könnte das Kapseln innerhalb der Methoden von Lock/Unlock jeweils innerhalb von lock {} mein Problem vielleicht schon beheben?
Grüße,
Chris
"Wenn Architekten genauso bauen würden, wie Programmierer programmieren, dann würde der erste Specht, der vorbeikommt, die Zivilisation zerstören." (Steven Weinberg)
Nein. Bitte richtig lesen.
Du musst die Liste synchronisieren, ansonsten rufst Du diese von mehreren Threads gleichzeitig auf und das geht eben nicht.
Siehe zB auch mein ähnlicher Blogbeitrag C# – Warteschlangen – Die BlockingCollection (Ob Queue<T> oder List<T> ist egal; beide müssen durch lock() manuell synchronisiert werden).
Dort siehst Du wie man lock() anwendet: entweder direkt auf die Liste oder mit Hilfe eines Synchronisations-Objekts.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hi,
das meinte ich etwa mit dem was ich schrieb. Vielen Dank, hat schön funktioniert 👍
Grüße,
Chris
"Wenn Architekten genauso bauen würden, wie Programmierer programmieren, dann würde der erste Specht, der vorbeikommt, die Zivilisation zerstören." (Steven Weinberg)