Laden...

Threadsicherer Getter, wie?

Erstellt von akswift vor 16 Jahren Letzter Beitrag vor 15 Jahren 3.770 Views
A
akswift Themenstarter:in
152 Beiträge seit 2005
vor 16 Jahren
Threadsicherer Getter, wie?

Hallo,
ich habe eine Property geschrieben mit der ich threadsicher einen Text setzen kann.
Allerdings beim Abrufen der Texteigenschaft brauche ich wohl auch Control.Invoke. Wie geht das den beim getter?


 public string TransponderData
        {
            get
            {
                 return Label.Text; // Wie hier Invoke? 
            }

            set
            {
                if (Label.InvokeRequired)
                {
                    Label.Invoke(
                        new EventHandler(delegate { Label.Text = value; })
                        );
                }
                else
                {
                    Label.Text = value;
                }
            }
        }

--MfG akswift

2.921 Beiträge seit 2005
vor 16 Jahren

Sehe ich nicht so. Nur das zugreifen in schreibender Weise müsste in diesem Falle auf jeden Fall threadsicher über Invoke sein. Nicht aber das auslesen.

Die Frage ist doch wohl eher ob ein anderer Thread gleichzeitig lesend zugreifen darf, da vielleicht ein anderer Thread noch die Daten setzt.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo akswift,

Control.Invoke liefert den Rückgabewert der aufgerufenen Methode als Objekt zurück. Du musst es also nur wieder auf den Zieltyp casten.

Verwende aber nicht EventHandler als Delegattyp. Ich denke, du kommt hier sogar ganz ohne expliziten Delegattyp aus.

Hallo dr4g0n76,

ist ja nur deswegen noch nicht threadsicher, weil das Invoke bei Getter noch fehlt. Und genau das war ja sein Frage, wie er das hinbekommt.

herbivore

2.921 Beiträge seit 2005
vor 16 Jahren

ist ja nur deswegen noch nicht threadsicher, weil das Invoke bei Getter noch fehlt. Und genau das war ja sein Frage, wie er das hinbekommt.

@herbivore: Natürlich. Du hast recht.

Seit der Erkenntnis, dass der Mensch eine Nachricht ist, erweist sich seine körperliche Existenzform als überflüssig.

A
akswift Themenstarter:in
152 Beiträge seit 2005
vor 16 Jahren

So gehts:


        delegate String TheDelegate();

        private string getTheString()
        {
            return Label.Text; 
        }
...
            get
            {
                if (Label.InvokeRequired)
                {
                  return (String)Label.Invoke(new TheDelegate( getTheString ));
                }
                else
                {
                    return Label.Text;
                }
            }
...

Aber das geht doch bestimmt noch schöner?

--MfG akswift

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo akswift,

ich denke, getTheString kannst du dir sparen:

return (String)Label.Invoke(new TheDelegate( delegate { return Label.Text; } ));

herbivore

A
akswift Themenstarter:in
152 Beiträge seit 2005
vor 16 Jahren

Danke,
so sieht es besser aus 🙂

--MfG akswift

J
222 Beiträge seit 2006
vor 16 Jahren

also ich habe eine Dll in der ich mir die wichtigsten delegates zusammengeschrieben habe und diese so immer threadsicher nutzen kann

kleiner auszug könnte auch für die intressant sein


        //--------------------------------------------------------------------------------
        private delegate string GetLabelTextDelegate(Form frm, Label lbl);
        private static string _GetLabelText(Form frm, Label lbl)
        {
            return lbl.Text;
        }
        /// <summary>
        /// Liefert den Text des gewählten Labels zurück
        /// </summary>
        /// <param name="frm">Form des Controls</param>
        /// <param name="lbl"> Control Name</param>
        /// <returns>Label.Text</returns>
        public static string GetLabelText(Form frm, Label lbl)
        {
            return frm.Invoke(new GetLabelTextDelegate(_GetLabelText), new object[] { frm, lbl }).ToString(); ;
        }
        //--------------------------------------------------------------------------------

aufzurufen dann zb: aktuelle form und labelname


MessageBox.Show(GetLabelText(this, label1));

vielleicht kannste das gebrauchen

lg Jabi

M
174 Beiträge seit 2006
vor 16 Jahren

Ich bin gerade auch dabei das erste mal etwas mit Threads zu machen, nachdem ich mich jetzt gestern und heute ein paar Stundenlang damit beschäftigt hab hier im Forum und auf anderen Seiten in das Thema einzulesen hab ich mich jetzt rangetraut damit loszulegen nur bin ich mir gerade nicht sicher mit einer Threadsicheren get / set Methode, bisher sieht sie so aus:


public string t_hauptfeld_text
{
   get
   {
      if( m_hauptfeld.InvokeRequired )
      {
         return (string)m_hauptfeld.Invoke( new XXX ( delegate { return m_hauptfeld.Text; } ) );
      }
      else
      {
         return m_hauptfeld.Text;
      }
   }
   set
   {
       if( m_hauptfeld.InvokeRequired )
      {
         m_hauptfeld.Invoke( new XXX ( delegate{ m_hauptfeld.Text = value; } ) );
      }
      else
      {
         m_hauptfeld.Text = value;
      }
   }
}

Für die XXX weiß ich nicht sicher was ich implementieren muss, wäre das beste ein
delegate string stringDelegate()? Oder gibts noch andere Lösungen? Oder passt bei dem ganzen sowieso was überhaupt nich?

Gruß Muphin

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo Muphin,

für das erste XXX musst du einen eigene Delegattyp definieren. Deine Definition delegate string stringDelegate() passt auf jeden Fall, auch wenn mir der Name nicht gefällt. Ich habe aber auch keinen besseren parat. 🙂

Für das zweite XXX kannst du MethodInvoker verwenden und solltest das auch tun.

herbivore

M
174 Beiträge seit 2006
vor 16 Jahren

vielen dank, habs so umgesetzt!


      private delegate string stringDelegate();
...
      public string t_hauptfeld_text
      {
         get
         {
            if( m_hauptfeld.InvokeRequired )
            {
               return (string)m_hauptfeld.Invoke( new stringDelegate( delegate { return m_hauptfeld.Text; } ) );
            }
            else
            {
               return m_hauptfeld.Text;
            }
         }
         set
         {
            stopwatch.Start();
            if( m_hauptfeld.InvokeRequired )
            {
               m_hauptfeld.Invoke( new MethodInvoker( delegate { m_hauptfeld.Text = value; } ) );
            }
            else
            {
               m_hauptfeld.Text = value;
            }
            stopwatch.Stop();
            float ms = 1000 * stopwatch.ElapsedTicks / (float)System.Diagnostics.Stopwatch.Frequency;
            writer.WriteLine( ms.ToString() );
         }
      }


4.207 Beiträge seit 2003
vor 15 Jahren

Um diese Abfrage mit dem InvokeRequired zu umgehen, siehe auch http://www.des-eisbaeren-blog.de/Blog/Permalink/2006/05/17/InvokeOnGUIThread.aspx

Natürlich muss die entsprechende Methode dann an der Form definiert sein, aber das halte ich sowieso für keine dumme Idee ...

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Golo Roden,

private void MyMethod(string text)
{
    BeginInvoke((MethodInvoker) delegate()
    {
        this.Text = text;
    });
}

Dadurch wird der Text allerdings asynchron gesetzt. Sprich, nachdem MyMethod verlassen wurde, kann man sich nicht darauf verlassen, dass der Text schon gesetzt ist. Das kann in bestimmten Situationen ein Nachteil sein. Es kann aber auch Vorteil sein, BeginInvoke zu verwenden, wenn man nämlich sehr viel Zugriffe mach, weil BeginInvoke deutlich billiger ist als Invoke.

herbivore

4.207 Beiträge seit 2003
vor 15 Jahren

Das stimmt - Du kannst an dieser Stelle natürlich genauso gut Invoke statt BeginInvoke verwenden - im Grunde genommen ging es mir nur um die Variante mit dem MethodInvoker-Delegat, und dass man sich so eben dieses nervive InvokeRequired schenken kann.

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Golo Roden,

ich meine mich zu erinnern, dass Invoke Deadlock einen verursacht, wenn es aus dem GUI-Thread aufgerufen wird. In meinem kleinen TestProgramm ist das allerdings nicht passiert. Der Code im Reflector weist eigentlich auch darauf hin, dass zumindest ein Deadlock entstehen könnte. Ich würde im Zweifel lieber BeginInvoke verwenden. Oder wenn es Invoke sein muss, eben doch vorher auf InvokeRequired prüfen.

herbivore

1.820 Beiträge seit 2005
vor 15 Jahren

Hallo!

Also ich finde Golo Roden's Weg sehr übersichtlich, kannte diesen Weg bisher noch nicht, und das mit der Asynchronität sollte in den meisten Fällen kein Problem darstellen, aber es ist gut, wenn man's weiß.

Andererseites hatte ich bisher auch mit Invoke nie Probleme.

Nobody is perfect. I'm sad, i'm not nobody 🙁

4.207 Beiträge seit 2003
vor 15 Jahren

Okay, das mit dem Deadlock wäre natürlich ungünstig ... wo genau liegt denn da das Problem?

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo Golo Roden,

so wie ich das sehe, schicken Invoke und BeginInvoke Nachrichten an die MessageQueue des Fensters, wobei Invoke zusätzlich auf die Antwort vom GUI-Thread wartet. Wenn aber Invoke nun aus dem GUI-Thread aufgerufen wird, werden ja solange keine Nachrichten verarbeitet, bis die Methode beendet ist. Also kann keine Antwort geschickt werden. Wenn aber keine Antwort kommt, beendet sich Invoke nicht/nie ==> Deadlock.

Warum es in meinem Beispielprogramm trotzdem geht, weiß ich nicht. Ich habe mir das aber auch nicht genauer angeguckt.

herbivore

3.971 Beiträge seit 2006
vor 15 Jahren

Hallo Herbivore,
ich denke ein Deadlock entsteht, wenn man BeginInvoke und anschließend EndInvoke aufruft. Grund ist der von dir beschriebene Fall mit der Queue.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo kleines_eichhoernchen,

komischerweise blockiert es selbst dann nicht, obwohl ich das genau wie du vermutet hätte.

Zumindest für Invoke kann ich auch schon sagen, warum es keinen Deadlock gibt. Weil Invoke mit

if ((SafeNativeMethods.GetWindowThreadProcessId(new HandleRef(this, this.Handle), out num) == SafeNativeMethods.GetCurrentThreadId()) && synchronous)

genau den Fall prüft, ob es aus dem GUI-Thread aufgerufen wurde. Invoke benutzt dann eben nicht PostMessage, sondern führt die Methode (mehr oder weniger) direkt aus.

herbivore