Object synchronization method was called from an unsynchronized block of code.
-
Hallo,
ich fürchte mein Verständnis für Multithreading reicht an der Stelle nicht aus, um das Problem zu lösen. Ich arbeite an einer Nachrichtenwarteschlange, die Nachrichten zwischenspeichern soll, die dann durch Klienten abgeholt werden sollen - ich denke das Prinzip ist jedem bekannt.
Nun fliegt mir in meiner Loggingklasse die im Titel genannte Exception während meines ersten Belastungstests um die Ohren. Ich kann bzw. darf die originalen Quellen leider nicht veröffentlichen
Daher habe ich ein Minimalbeispiel geschrieben, welches das Problem zumindest theoretisch veranschaulichen soll. Warum theoretisch? Hm... nun, im Beispiel tritt der Fehler bisher nicht auf. Die Ursache konnte ich bisher nicht ermitteln. Aber ich hoffe, dass es für das Verständnis meines Fehlers helfen kann.
Eine ganz simple Logger-Klasse:
namespace ThreadTest { internal class Logger { public static Logger UniqueInstance { get { InternalUniqueInstance ??= new(); return InternalUniqueInstance; } } private static Logger? InternalUniqueInstance; private readonly Mutex Mutex = new(); private Logger() { } public void Write(string message) { try { if (Mutex.WaitOne()) { Console.WriteLine(message); } } finally { this.Mutex.ReleaseMutex(); } } } }
Eine ganz simple HttpGetKlasse mit Methode, um den Request auszuführen:
namespace ThreadTest { internal class HttpGetRequest(string uniformResourceLocator) { private readonly string UniformResourceLocator = uniformResourceLocator; private string? Result; public void Perform() { var logger = Logger.UniqueInstance; logger.Write("Perform has been called"); try { var httpClient = new HttpClient(); var response = httpClient.GetAsync(this.UniformResourceLocator).Result; this.Result = response.Content.ReadAsStringAsync().Result; } catch (Exception exception) { logger.Write(exception.ToString()); } } } }
Der eigentliche Belastungstest:
namespace ThreadTest { internal class Program { private static readonly int NThreads = 8 * 1024; static void Main(string[] args) { var logger = Logger.UniqueInstance; var threadList = new List<Thread>(); logger.Write("Start ..."); for (var index = 0; index < NThreads; index++) { var request = new HttpGetRequest("https://www.google.de"); var thread = new Thread(new ThreadStart(request.Perform)); threadList.Add(thread); logger.Write("New thread created and added"); } for (var index = 0; index < NThreads; index++) { threadList[index].Start(); logger.Write(string.Format("Thread {0} started", index)); } for (var index = 0; index < NThreads; index++) { threadList[index].Join(); } logger.Write("Done"); } } }
Die Exception fliegt beim <this.Mutex.ReleaseMutex()> in der Logger-Klasse beim Schreiben der Nachricht in der Methode <Write()>.
Mir fehlt hier ein bisschen das Verständnis des Multithreadings - denke ich jedenfalls. Ich habe mit async und await experimentiert, aber das hat keine Verbesserung gebracht.
Kann mir jemand einen Tipp geben?
VG Torsten
-
Beim ersten Überfliegen ist mir nur die Zeile 32 in der Logger-Klasse aufgefallen (
this.Mutex.ReleaseMutex
). Das sollte doch nur aufgerufen werden, wenn der Codeabschnitt den Mutex auch gelockt hat, also eher:public void Write( string message ) { if( Mutex.WaitOne() ) { try { Console.WriteLine( message ); } finally { Mutex.ReleaseMutex(); } } }
Oder täusche ich mich da?
PS:
Warum einmalMutex.WaitOne()
und dannthis.Mutex.ReleaseMutex()
?PPS:
Hab den try-finally Block einfach verschoben, ich habe keine Ahnung, wo da Exceptions geworfen werden könnten. Muss man ggf. wieder geradebiegen.
-
Hallo,
@DocShoe Hm... okay, ich habe das immer so gemacht. Ich meine, dass ist rein programm-technisch das gleiche. Aber ich lasse mich gerne eines Besseren belehren
Die Exception fliegt wie oben beschrieben in der Zeile mit dem ReleaseMutex().
VG Torsten
-
Kann das nicht der Grund sein?
WaitOne
lockt aus irgendwelchen Gründen den Mutex nicht und wirft dabei eine Exception. Die ignorierst du und im finally-Block gibst du den Mutex wieder frei, obwohl er durch den aktuellen Thread nie gelockt wurde. Ergänz` deinen Code mal um einen Exception Handler und guck, ob dein Fehler auftritt, wenn eine Exception geworfen wurde (oderMutex.WaitOne
aus irgendwelchen Gründenfalse
zurückgibt.
-
Hallo nochmal,
also ja, du hast Recht und mir damit sehr geholfen. Ich hatte mit der Aussage "es sei das gleiche" einen echt doofen Denkfehler. Ich muss dazu sagen, dass ich im originalen Programm einen Timeout beim Mutex mitgebe, und genau der tritt nämlich auf. Ich habe es jetzt in der einen Methode des Loggings umgebaut und erhalte die Exception nicht mehr.
Ich verbeuge mich höflich und sage nochmals "Danke"
VG Torsten
-
@TorDev sagte in Object synchronization method was called from an unsynchronized block of code.:
Ich muss dazu sagen, dass ich im originalen Programm einen Timeout beim Mutex mitgebe, und genau der tritt nämlich auf.
Siehste, und genau deswegen sollte man nie modifizierten Code posten. Auf ein minimales Beispiel reduzieren, ja, das ist gut. Aber der Fehler muss dabei halt noch auftreten.