none
Problem mit Threading RRS feed

  • Frage

  • Hallo,

    wie haben eine MySql Datenbank für einen CalDav Kalender auf dem Server am Laufen. Die Tabellen (z.B. "calendarobjects") können wir von unserem PVS (PraxisVerwaltungsSoftware) aus beschreiben und über gespeicherte IDs auch wieder lesen. Die grafische Darstellung der Einträge überlassen wir Thunderbird.

    Der Start des gesamten Prozesses sind Änderungen in einem DGV. Mein Personal legt hier neue Termine an, verändert oder löscht auch welche. Mit dem Speichern gehe ich alle rows durch (For Each rw in t.Rows) und prüfe auf added, modified und deleted. Von der jeweiligen Row springe ich die Sub DavCalendarUpdate an. In dieser erzeuge ich eine neue Instanz der Klasse CalendarUpdater und starte hier einen neuen Thread.

    Private Sub DavCalendarUpdate(dt As String, pat As String, krz As String, descr As String, t171id As Int32, uid As String, mode As String) If krz.Trim = "" Then Exit Sub If krz.Split(" "c).Length = 0 Then Exit Sub Dim cu As CalendarUpdater = New CalendarUpdater("admin", "admin", calHost) cu.datum = CDate(dt) cu.patient = pat cu.kuerzel = krz cu.descript = descr cu.record = t171id cu.mode = mode cu._id = uid cu.termine = (iDBS.CountRecords1("SELECT COUNT(*) FROM t171 WHERE C1711='" & dt & "' AND C1714='" & krz & "'") - 1) * 10 Dim t As Threading.Thread = New Threading.Thread(New Threading.ThreadStart(AddressOf cu.Save)) t.Start() End Sub

    Public Class CalendarUpdater
        Public Sub New(user As String, password As String, mHost As String)
            Me.credentials = New NetworkCredential(user, password)
            Me.host = "http://" & mHost
        End Sub

        Public Sub Save()
            calendar = kuerzel.Trim.Split(" "c)(0).ToUpper
            Select Case mode
                Case "insert" : Insert()
                Case "update" : Update()
                Case "delete" : Delete()
            End Select
        End Sub

    Verkürzt sieht die Routine Insert() z.B. so aus:

            ev = New [Event]
            ev.Start = New iCalDateTime(datum.Year, datum.Month, datum.Day, hour(0), hour(1), 0)
            ev.[End] = ev.Start.AddMinutes(8)
            ev.Summary = patient
            ev.Location = categorie
            ev.Description = descript
            ev.Categories = New String() {categorie}
    
            Dim uri As New Uri(String.Format("{0}/sabredav/calendarserver.php/calendars/admin/{1}/{2}.ics", host, calendar, ev.UID))
            Dim request As HttpWebRequest = CType(WebRequest.Create(uri), HttpWebRequest)
            request.Method = "PUT"
            request.ContentType = "text/calendar; charset=utf-8"
            request.Credentials = credentials
            Try
                Using reqStream As Stream = request.GetRequestStream()
                    Dim reqBodyData As Byte() = GetBytesFromEvent(ev)
                    reqStream.Write(reqBodyData, 0, reqBodyData.Length)
                    reqStream.Close()
                    Try
                        Dim response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
                    Catch ex As Exception
                        MessageBox.Show(ex.Message)
                        Exit Sub
                    End Try
                End Using
            Catch we As WebException
                MessageBox.Show(we.Message & Environment.NewLine & "Keine Verbindung zum Kalender-Server!")
                Exit Sub
            End Try

    Mit der request Methode PUT wird ein neues Kalender Objekt eingefügt. Thunderbird bemerkt dies und macht einen Refresh.

    Das funktioniert, solange im DGV nur eine einzelne Zeile verändert wird. Mittlerweile sind meine Schwestern aber so clever, dass sie gleich mehrere Zeilen im Grid ändern. Und nun kommt es zu Störungen. Beim Debuggen kann ich kaum noch nachverfolgen, wo der Cursor überall hin und her springt. Beim For Each rw in t.Rows wird ja wohl jedesmal ein neuer Thread gestartet. Und das, obwohl der vorhergehende noch nicht beendet ist. Das gipfelt schliesslich im Catchblock mit einer WebException.

    Ich denke, es knallt wegen Thunderbird. Ich habe nach einer Möglichkeit gesucht, ihn während der Thread-Ausführung vorübergehend lahm zu legen. Hab aber nichts dazu gefunden. Mit Async und Await komme ich immer noch nicht so richtig klar. Aber ich glaube, damit könnte es funktionieren. Kann uns jemand bei diesem Problem behilflich sein? Danke.

    Viele Grüße

    Norbert



    • Bearbeitet norbert3 Donnerstag, 11. Mai 2023 13:22
    Donnerstag, 11. Mai 2023 13:20

Alle Antworten

  • Hallo Norbert,

    In diesem Thread wird so zu sagen die Parallelisierung in der entgegengesetzten Richtung (Datenrasteransicht wird mit einer Datentabelle befüllt) behandelt:
    Multiple threading with DataGridView

    Was das Speichern betrifft, so könnte z. B. die Datenquelle als eine Datentabelle (DataTable-Klasse) oder, wenn es um eine einzelne Zeile geht, als Datenzeile (DataRow-Klasse) gespeichert werden, damit nicht die gesamte Datenrasteransicht durchgegangen werden muss. Mit der Syntax der Lambdaausdrücke in Visual Basic.NET kenne ich mich jedoch nicht so gut aus und hoffe, dass jemand, der damit größere Erfahrung oder andere Vorschläge hat, sich in den Thread einschaltet.

    Gruß,
    Dimitar


    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „IT-Pros helfen IT-Pros“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Freitag, 12. Mai 2023 08:07
    Administrator
  • Danke Dimitar,

    das Grid und die Table sind nicht das Problem. Die gesamte Datenansicht muss ich durchgehen, aber die geänderten Rows sind nur wenige. In 90 % nur eine, in 9 % zwei und ganz selten mal mehr. Und nur bei diesen wird ein Thread zur Verarbeitung im CalDav gestartet.

    >> dass jemand, der damit größere Erfahrung oder andere Vorschläge hat, sich in den Thread einschaltet

    Das würde mich sehr freuen!

    Freitag, 12. Mai 2023 11:05
  • Hallo Norbert,

    ohne mich jetzt im Detail mit deinem Problem befasst zu haben, eine mögliche Lösung wäre wahrscheinlich das Producer-Consumer-Pattern  vereinfacht geschildert in der DavCalendarUpdate Methode (Producer) wird einmalig ein Thread (Consumer) gestartet und alle folgende Aufrufe legen ihre Aufgabe in einer Liste/Queue ab und der gestartete Thread arbeitet diese Liste Auftrag für Auftrag ab. Aber Achtung die Liste muss Threadsafe sein (wie z.B. eine BlockingCollection)!

    VG Alpecine

    Samstag, 13. Mai 2023 06:48
  • In Deinem verlinkten Beitrag wird das Procedere sehr anschaulich dargestell. Das Prinzip ist mir jetzt klar geworden. Aus den sehr kurzen Beispielen in C++ und Java kann ich aber leider nicht entnehmen, wie ich das konkret in VB umsetzen muss. Der lesende "Verbraucherprozess" ist ja Thunderbird. Und wie soll ich dem beibringen, was er zu einem gegebenen Moment tun darf und was nicht. Da gibt es noch schlaflose Nächte ...

    Montag, 15. Mai 2023 15:10
  • Hallo Norbert,

    ich versuche dir hier das mal schematisch dar zustellen:

    'Membervariablen Dim Queue Dim ConsumerThread Private Sub DavCalendarUpdate(...) '<- Producer Auftrag (Instanz von CalendarUpdater) in Queue ablegen (z. B. BlockingCollection) If ConsumerThread beendet? Then Ja dann starte den ConsumerThread T(..ThreadStart(AddressOf cu.Save))

    End If End Sub Public Class CalendarUpdater Public Sub Save() '<-- Consumer (läuft in einem eigenen Thread) While (Ist ein Auftrag in der Queue?) Bearbeite den Auftrag aus der Queue (Die benötigten Infos können ja dem Auftrag entnommen werden). ... Select Case Auftrag.mode Case "insert" : Auftrag.Insert() Case "update" : Auftrag.Update() Case "delete" : Auftrag.Delete() End Select .... End While End Sub End Class

    Das ist jetzt stark vereinfacht. Aber so erreichst du das die Aufträge zu Thunderbird nacheinander (es läuft ja nur ein Thread der mit Thunderbird kommuniziert) abgearbeitet werden.
    Du kannst auch nach BlockingCollection im I-Net suchen, da gibt es zahllose Beispiele.


    • Bearbeitet Alpecin Mittwoch, 17. Mai 2023 06:36
    Mittwoch, 17. Mai 2023 06:36
  • Danke! Hab gerade ne Auszeit und bin ein paar Tage im Erholungsurlaub. Wenn ich zurück bin, steht eine komplizierte Softwarezertifizierung an. Danach geht es sofort an CalDav. Hast mir ein sehr schönes und verständliches Gerüst gebaut. Da ich jedoch von BlockingCollection noch nie etwas gehört habe, befürchte ich, dass ich da wahrscheinlich ne Menge Einarbeitung brauche ...

    Viele Grüße

    Norbert

    Mittwoch, 17. Mai 2023 11:34