Threadsichere Warteschlange

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von mbfan.

    Threadsichere Warteschlange

    Guten Abend,
    ich habe ein kleines Problem damit, eine brauchbare Warteschlange für das Abarbeiten von Methoden zu erstellen, die threadsicher funktionieren sollte.
    Ausgangslage ist der Bug, dass Newtonsoft.Json unter Mono bei gleichzeitigem Aufruf aus mehreren Threads unter (für mich noch nicht ganz klaren) Umständen das ganze Programm zum Abstürzen bringt.

    VB.NET-Quellcode

    1. Public Class JSONManagement
    2. Shared initialized As Boolean = False
    3. Shared jss As JsonSerializerSettings
    4. Public Shared actID As ULong = 0
    5. Public Shared nextID As ULong = 0
    6. Public Shared Function toJSON(Of T)(ByVal obj As T) As String
    7. init()
    8. wait()
    9. Dim s As String = ""
    10. Try
    11. s = JsonConvert.SerializeObject(obj, jss)
    12. 'Console.WriteLine("JSONSer: " & s)
    13. Catch ex As Exception
    14. Throw ex
    15. End Try
    16. actID += CULng(1)
    17. Return s
    18. End Function
    19. Public Shared Function fromJSON(Of T)(ByVal obj As Object) As T
    20. init()
    21. wait()
    22. Dim ret As T = Nothing
    23. Try
    24. 'Console.WriteLine("JSONDeS: " & obj.ToString)
    25. ret = JsonConvert.DeserializeObject(Of T)(obj.ToString, jss)
    26. Catch ex As Exception
    27. Throw ex
    28. End Try
    29. actID += CULng(1)
    30. Return ret
    31. End Function
    32. Private Shared Sub wait()
    33. Dim myID As ULong = nextID
    34. nextID += CULng(1)
    35. Dim count As Integer = 0
    36. Do Until myID <= actID Or count > 100
    37. System.Threading.Thread.Sleep(0)
    38. count += 1
    39. Loop
    40. End Sub
    41. Private Shared Sub init()
    42. If initialized = True Then
    43. Exit Sub
    44. End If
    45. jss = New JsonSerializerSettings()
    46. jss.ContractResolver = New PrivateContractResolver
    47. jss.StringEscapeHandling = StringEscapeHandling.EscapeNonAscii
    48. initialized = True
    49. End Sub
    50. End Class

    Mein bisheriger Versuch ist wie im Code oben. Jedoch führt dies dazu, dass oft die aktuell abzuarbeitende ID um bis zu 100 höher ist als die aktuell "neue" ID - und dass sich das ganze teilweise komplett aufhängt. Ich muss dazu sagen, dass ich bis zu 400-600 Vorgänge pro Sekunde habe.
    Hat dort jemand möglicherweise einen Vorschlag, wie das ganze besser lösbar wäre?
    Nico
    You should live for that what you belive. - Drag-Drop Beschreibung
    Hi
    schau' dir mal Interlocked und SyncLock an. Erst wenn du mit Interlocked an deine Grenzen stößt, solltest du in deinem Problem auf SyncLock zurückgreifen. Du kannst aber auch Mutex verwenden.

    Das Thread.Sleep finde ich hässlich, bzw. die komplette Schleife würde ich löschen, da sich count ja unmittelbar aus der Bedingung ergeben sollte. Catches sind übrigens recht teuer. Außerdem würde ich die Exception eher als inner exception weitergeben, als sie zu rethrowen, um den Stacktrace zu löschen.

    Es gibt übrigens ein Shared Sub New() das als statischer Konstruktor herhalten kann. Das kannst du statt deiner nicht-threadsicheren init-Methode einbauen.
    Konstanten in ULong kannst du durch den Suffix UL angeben: 1UL. Eine Zuweisung von s = "" ist übrigens unnötig und statt "" schreibt man idR. String.Empty.

    Ach ja, was ebenfalls hilfreich sein kann: Das ThreadStaticAttribute-Attribut. Das kannst du verwenden, um Felder pro Thread anders zu besetzen. Beachte aber, dass du sie dafür in einer Property kapseln musst, die in jedem Thread eine Initialisierung durchführt. Der statische Konstruktor wird nur für den ersten Thread ausgeführt, der auf die Klasse zugreift.

    Viele Grüße
    ~blaze~
    Vielen Dank für die ausführlichen Tipps :)
    Ich werde mal schauen wie es morgen zur Rushhour auf dem Server aussieht, der diese Methode benutzt ;)
    Meine aktuelle Lösung

    VB.NET-Quellcode

    1. ​Public Class JSONManagement
    2. Private Shared initialized As Boolean = False
    3. Private Shared jss As JsonSerializerSettings
    4. Public Shared Function toJSON(Of T)(ByVal obj As T) As String
    5. init()
    6. Dim s As String = ""
    7. SyncLock jss
    8. Try
    9. s = JsonConvert.SerializeObject(obj, jss)
    10. 'Console.WriteLine("JSONSer: " & s)
    11. Catch ex As Exception
    12. Throw New Exception("Error beim Serialisieren", ex)
    13. End Try
    14. End SyncLock
    15. Return s
    16. End Function
    17. Public Shared Function fromJSON(Of T)(ByVal obj As Object) As T
    18. init()
    19. Dim ret As T = Nothing
    20. SyncLock jss
    21. Try
    22. 'Console.WriteLine("JSONDeS: " & obj.ToString)
    23. ret = JsonConvert.DeserializeObject(Of T)(obj.ToString, jss)
    24. Catch ex As Exception
    25. Throw New Exception("Error beim Deserialisieren", ex)
    26. End Try
    27. End SyncLock
    28. Return ret
    29. End Function
    30. Shared Sub New()
    31. jss = New JsonSerializerSettings()
    32. jss.ContractResolver = New PrivateContractResolver
    33. jss.StringEscapeHandling = StringEscapeHandling.EscapeNonAscii
    34. initialized = True
    35. End Sub
    36. Private Shared Sub init()
    37. If initialized = True Then
    38. Exit Sub
    39. End If
    40. jss = New JsonSerializerSettings()
    41. jss.ContractResolver = New PrivateContractResolver
    42. jss.StringEscapeHandling = StringEscapeHandling.EscapeNonAscii
    43. initialized = True
    44. End Sub
    45. End Class


    mbfan
    You should live for that what you belive. - Drag-Drop Beschreibung
    Vielleicht ist es auch sinnvoll, jedem Thread seinen eigenen JSON-Serializer mitzugeben, womöglich hilft das.

    ​Wenn das ein Reentranz-Problem ist, wirst du das nie und nimmer hinbekommen, dann darf tatsächlich nur ein einziger JSON-Serializer laufen.
    ConcurrentQueue war auch mein erster Gedanke ;) Aber beim Studium des Sourcecodes tippe ich hier auf ein Reentranz-Problem, das mit gar keiner irgendwie gearteten Queue (außer sie serialisiert den Serializer - was für ne Formulierung =O ) lösbar ist. Es darf dann zwingend nur ein JSON-Serializer arbeiten.
    Die Queue an sich ist auf überflüssig - außer man führt das Programm unter Mono aus - da hat Newtonsoft den Bug, dass es teilweise abstürzt wenn es mehrmals gleichzeitig aufgerufen wird. (Quelle: stackoverflow.com/questions/21…t-json-in-linux-with-mono)
    You should live for that what you belive. - Drag-Drop Beschreibung

    Quellcode

    1. ​# mono --version
    2. Mono JIT compiler version 5.2.0.215 (tarball Mon Aug 14 15:50:54 UTC 2017)
    3. Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
    4. TLS: __thread
    5. SIGSEGV: altstack
    6. Notifications: epoll
    7. Architecture: amd64
    8. Disabled: none
    9. Misc: softdebug
    10. LLVM: supported, not enabled.
    11. GC: sgen (concurrent by default)

    Sollte die aktuellste sein, oder?
    You should live for that what you belive. - Drag-Drop Beschreibung