Zuviele Delegaten....



  • Hallo Forum,
    ich habe in einer App viele Threads, die aus verschiedener Hardware Daten ziehen. Jetzt besitzt die App auch eine richtextBox, in welcher Meldungen aus diesen Threads angezeigt werden. Bisher habe ich dazu für jeden Thread einen eigenen Delegaten angelegt, der in etwas die folgende Form hat:

    public delegate void ControlStringConsumer(string text, int control);  
            public void SetText(string text, int control)
            {
                if (this.richTextBox1.InvokeRequired)
                {
                    this.Invoke(new ControlStringConsumer(SetText), new object[] { text, control });  
                }
                else
                {
                    switch (control)
                    {
    
                        case 0: richTextBox1.Text = text + "\n" + richTextBox1.Text;
                            break;
                        case 1: richTextBox1.Text = "";
                            break;
                        default:
                            break;
                    }
                }
    
            }
    

    Da ich nun für jeden dieser Threads so eine Definition anlege – etwa SetText1, SetText2 usw., sammelt sich da viel Code an. Verwende ich den selben Delegaten für zwei asynchrone Threads, so kommt es natürlich zu einer Zugriffsverletzung, wenn beide Threads mal gleichzeitig auf den Delegaten zugreifen.
    Was kann ich also tun, um diese vielen Definitionen zu vereinfachen oder zu vermeiden?



  • 7x7-7 schrieb:

    Verwende ich den selben Delegaten für zwei asynchrone Threads, so kommt es natürlich zu einer Zugriffsverletzung, wenn beide Threads mal gleichzeitig auf den Delegaten zugreifen.

    Äh. Kann sein dass ich dich nicht richtig verstehe, aber wenn doch, dann wüsste ich keinen Grund warum es da zu einer Zugriffsverletzung kommen sollte. Mal ganz davon abgesehen dass man Zugriffsverletzungen in C# echt nur sehr schwer hinbekommt so lange man kein unsafe , PInvoke o.ä. verwendet -- aber ich kann mir auch kein anderes Problem vorstellen.

    Oder geht es dir darum dass die verschiedenen Threads nicht alle in die selbe RichtextBox rein schreiben sollen, sondern in unterschiedliche?

    Kannst du vielleicht zeigen wie der "geht so nicht, weil" Code den du dir vorstellst aussehen würde, und was deiner Meinung nach das "weil" dabei ist?



  • Hallo,
    und danke für den Beitrag. Ich habe gerade mal eine Testapp gebaut – und ja – in C# scheint es tatsächlich damit erstmal kein Problem zu geben. Was ich hätte vielleicht noch sagen sollen ist, dass alle Threads in Klassenbibliotheken untergebracht sind, die dann auf diesem Weg: https://www.c-plusplus.net/forum/340703 mit der C# APP kommunizieren, die dann als GUI fungiert.
    Irgendwann hing sich das Ganze immer mal wieder auf, so dass ich ab fortan jedem Thread seinen eigenen Delegaten zum Bedienen der GUI Grafikelemente zugeordnet habe und alles sodann absturzfrei funktionierte.
    Ich habe leider nur noch dunkel in Erinnerung, dass es sich bei der Ursache des Absturzes um eine Zugriffsverletzung auf den Delegaten handelte.
    Ausgehend davon sammeln sich mittlerweile um die 50 Delegaten zum Bedienen von z.B. einer richtextBox an, was ich wie anfangs gesagt vereinfachen wollte.


  • Administrator

    Es bleibt dabei was hustbaer gesagt hat.

    Das einzige was mir noch auffällt vom vorherigen Thread: Wieso verwendest du Marshal.GetFunctionPointerForDelegate ? Du kannst einen Delegate in C++/CLI definieren und den in deinem C# Programm verwenden und deiner Bibliothek direkt übergeben. Da musst du keinen Umweg über einen Funktionszeiger gehen.

    Oder du könntest ein gemeinsames Interface definieren, welches vom C# Programm angeboten wird und von deinen Klassenbibliotheken verwendet wird. Allenfalls in einer weiteren Bibliothek, welche von deinem C# Programm und deinen bisherigen Bibliotheken verwendet wird.



  • Bei GetFunctionPointerForDelegate ist eines ganz wichtig, was auch in der Doku steh (leider nicht fett rot und blinkend, denn da wäre es angebracht):

    You must manually keep the delegate from being collected by the garbage collector from managed code. The garbage collector does not track references to unmanaged code.

    Wenn du dich daran nicht hältst, dann kannst du wirklich Crashes (Access-Violations) bekommen.
    Der GC kann nämlich nicht wissen ob dein native Code den Funktionszeiger noch braucht. Trotzdem muss er Delegates (plus deren Native-Call-Thunks auf die die Funktionszeiger zeigen) irgendwie collecten können -- sonst würden die ja auf ewig leaken.

    Daher die Regel: Du musst im managed Teil eine Referenz auf den Delegate behalten, so lange der Funktionszeiger im native Teil verwendet werden kann.

    Folgendes ist also z.B. FALSCH:

    public void MyMethod()
    {
        MyDelegateType dlg = new MyDelegateType(this.MySomethingHandler);
        MyPInvokeStuff.SetFunctionPtr(Marshal.GetFunctionPointerForDelegate(dlg));
    }
    

    Wenn die native Komponente danach den Funktionszeiger verwendet, kann es krachen.
    Damit es funktioniert, musst du sicherstellen dass das Objekt auf das dlg nicht collected werden kann. z.B. indem du ne Membervariable machst die darauf verweist o.ä.

    Wobei sich daran nichts ändert nur weil man MySomethingHandler zwei oder dreifach implementiert.



  • Moin,

    du kannst mit lock den gleichzeitigen Zugriff verhindern

    private object lockObject = new object();
    public delegate void ControlStringConsumer(string text, int control);  
    public void SetText(string text, int control)
    {
         lock(lockObject)
         {
              // hier dann dein Code
         }
    }
    

    Das sorgt dafür, dass der Code nicht mehr als einmal zur gleichen Zeit ausgeführt wird.



  • @rollover
    Wofür soll das gut sein? Welches Problem willst du hier bei diesem Beispiel damit lösen?



  • hustbaer schrieb:

    @rollover
    Wofür soll das gut sein? Welches Problem willst du hier bei diesem Beispiel damit lösen?

    Das zu viele Threads gleichzeitg auf die Textbox zugreifen wollen.



  • Hä?
    Du darfst immer nur aus dem GUI Thread auf die TextBox zugreifen. Das ist genau einer, der GUI Thread (für dieses Fenster).
    Und genau einer kann nicht "zu viele" sein.

    Und damit kein anderer Thread zugreift ist da ja das InvokeRequired/Invoke.

    Also nochmal: Welches Problem willst du damit lösen?



  • Hallo Forum,
    vielen Dank für die vielen Beiträge zu meiner Frage.
    @ Hustbaer: Ja das ist richtig – die Referenzen aller Delegaten sind daher auch in der GUI Klasse e.g. Form1 während der gesamten Laufzeit gespeichert.

    Gut – ich werde mal die App in der das Problem einer Zugriffsverletzung im Zusammenhang mit dem Zugriff auf eine richtextbox über Delegaten auftrat nochmal vorkramen und genauer untersuchen. Vielleicht gab es ja doch eine andere Ursache – wenn nicht, dann melde ich mich hier wieder zurück.
    MfG.