myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Gemeinschaft » .NET-Komponenten und C#-Snippets » Komponenten mit mehreren Threads
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

Komponenten mit mehreren Threads

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
myCSharp.de
Moderationshinweis von herbivore (15.06.2008 20:51):

Dies ist ein Thread, auf den aus der FAQ verwiesen wird. Bitte keine weitere Diskussion, sondern nur wichtige Ergänzungen und diese bitte knapp und präzise. Vielen Dank!
 
Programmierhans
myCSharp.de-Poweruser/ Experte

avatar-1651.gif


Dabei seit: 05.04.2005
Beiträge: 4.221
Entwicklungsumgebung: VS2003-VS2013 / SAP WebIDE
Herkunft: Zentralschweiz


Programmierhans ist offline

Komponenten mit mehreren Threads

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ein Typisches Threading-Problem.

Eine Komponente verwendet mehrere Threads um die gewünschten Aufgaben erledigen zu können.

Wenn nun aus einem dieser Threads ein Event geworfen wird, dann trifft dieser im Calling-Thread ein (also nicht im UI-Thread). Dies ist eigentlich kein Problem... zum Problem wird es erst dann, wenn man in der Eventbehandlung versucht auf die UI-Controls zuzugreifen.....

Zugriffe auf das UI sind nur aus dem UI-Thread erlaubt......

Um nun nicht bei jedem Event überprüfen zu müssen (mittels this.InvokeRequired) habe ich eine Komponenten-Basisklasse geschrieben, welche dies per Default macht.

Die Basisklasse

C#-Code:
using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Windows.Forms;

namespace SynchronComponent
{
    /// <summary>
    /// SynchronComponentBase
    ///
    /// Basisklasse für alle Komponenten mit eigenen Threads
    ///
    /// Author:  Programmierhans
    /// Source:  [url=http://www.mycsharp.de/wbb2/thread.php?threadid=13144]Komponenten mit mehreren Threads[/url]
    /// </summary>
    public class SynchronComponentBase : System.ComponentModel.Component
    {
        #region Fields
        //Das Synchronisier-Objekt
        private ISynchronizeInvoke _SynchronizingObject=null;
        private System.ComponentModel.Container components = null;
        #endregion

        #region Constructors / IDisposable
        //Default - Constructor für den Designer
        public SynchronComponentBase(System.ComponentModel.IContainer container)
        {
            container.Add(this);
            InitializeComponent();
        }

        //Default - Constructor
        public SynchronComponentBase()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="pSynchronizingObject">das Objekt auf welches die Events gemarshalled werden sollen</param>
        public SynchronComponentBase(ISynchronizeInvoke pSynchronizingObject)
        {
            InitializeComponent();
            this._SynchronizingObject=pSynchronizingObject;
        }

        public SynchronComponentBase(System.ComponentModel.IContainer container, ISynchronizeInvoke pSynchronizingObject)
        {
            container.Add(this);
            InitializeComponent();
            this._SynchronizingObject=pSynchronizingObject;

        }

        /// <summary>
        /// Verwendete Ressourcen bereinigen.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if(components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }
        #endregion

        #region Vom Komponenten-Designer generierter Code
        /// <summary>
        /// Erforderliche Methode für die Designerunterstützung.
        /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
        /// </summary>
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }
        #endregion

        #region Overridden Properties

        /// <summary>
        /// Dies ist das Objekt in welches die Events gemarshalled werden
        /// </summary>
        public ISynchronizeInvoke SynchronizingObject
        {
            get{return this._SynchronizingObject;}
            set{this._SynchronizingObject=value;}
        }


        /// <summary>
        /// Trick 77 Abschnitt 5 .... wird hier verwendet um das Synchronizing-Object
        /// (also den Parent worauf die Komponente liegt)
        /// beim ziehen auf das Form automatisch zu setzen
        /// </summary>
        public override ISite Site
        {
            get
            {
                return base.Site;
            }
            set
            {
                base.Site = value;
                if (this.Site!=null)
                {
                    System.ComponentModel.Design.IDesignerHost host = (System.ComponentModel.Design.IDesignerHost)value.GetService (typeof (System.ComponentModel.Design.IDesignerHost));
                    if (host!=null)
                    {
                        Control ctrlParent = host.RootComponent as Control;
                        if (ctrlParent!=null)
                        {
                            this._SynchronizingObject=ctrlParent;
                        }
                    }
                }
            }
        }
        #endregion

        #region Protected Methods
        /// <summary>
        /// Diese Methode dient dazu den Event (sofern SynchronizingObject gesetzt ist) im richtigen
        /// Thread zu feuern
        /// </summary>
        /// <param name="pEventHandlerInstance">Der EventHandler dessen Event geworfen werden soll</param>
        /// <param name="sender">der sender-Parameter für den Event</param>
        /// <param name="e">Die EventArgs für den Event</param>
        protected void InvokeEventOnUIThread(EventHandler pEventHandlerInstance,object sender, EventArgs e)
        {
            //Wenn der Event-Abonniert ist
            if (pEventHandlerInstance!=null)
            {
                if (this._SynchronizingObject!=null && this._SynchronizingObject.InvokeRequired)
                {
                    //wirf den Event im richtigen (UI)-Thread
                    this._SynchronizingObject.Invoke(pEventHandlerInstance,new object[]{sender,e});
                }
                else
                {
                    //Da kein Synchronizer da ist wirf den Event im Calling-Thread (also nicht zwingend
                    //im UI-Thread)... dies ist das .Net-Default-Verhalten
                    pEventHandlerInstance(sender,e);
                }
            }
        }
        #endregion
    }
}

Eine Vererbung dieser Basisklasse kann z.B: so aussehen

C#-Code:
using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Threading;

namespace SynchronComponent
{
    /// <summary>
    /// Zusammenfassung für SampleComponent.
    ///
    /// Beispiel für eine Synchronisierte Komponente mit eigenen Threads
    ///
    /// Author:  Programmierhans
    /// Source:  [url=http://www.mycsharp.de/wbb2/thread.php?threadid=13144]Komponenten mit mehreren Threads[/url]
    /// </summary>
    public class SampleComponent : SynchronComponent.SynchronComponentBase
    {
        #region Fields
        /// <summary>
        /// Erforderliche Designervariable.
        /// </summary>
        private System.ComponentModel.Container components = null;
        #endregion

        #region Constructor / IDisposable
        public SampleComponent(System.ComponentModel.IContainer container)
        {
            ///
            /// Erforderlich für Windows.Forms Klassenkompositions-Designerunterstützung
            ///
            container.Add(this);
            InitializeComponent();

            //
            // TODO: Fügen Sie den Konstruktorcode nach dem Aufruf von InitializeComponent hinzu
            //
        }

        public SampleComponent()
        {
            ///
            /// Erforderlich für Windows.Forms Klassenkompositions-Designerunterstützung
            ///
            InitializeComponent();

            //
            // TODO: Fügen Sie den Konstruktorcode nach dem Aufruf von InitializeComponent hinzu
            //
        }

        /// <summary>
        /// Verwendete Ressourcen bereinigen.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if(components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }
        #endregion

        #region Vom Komponenten-Designer generierter Code
        /// <summary>
        /// Erforderliche Methode für die Designerunterstützung.
        /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
        /// </summary>
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }
        #endregion

        #region Events
        /// <summary>
        /// Dies ist der Event
        /// </summary>
        public event EventHandler TestEvent;
        #endregion

        #region Public Methods
        /// <summary>
        /// Löst den TestEvent aus einem anderen Thread aus
        /// aber im UI-Thread !
        /// </summary>
        public void OnTestInOtherThreadSynchronized()
        {
            new Thread(new ThreadStart(this.OnTestEventSynchronized)).Start();
        }

        /// <summary>
        /// Löst den TestEvent in einem anderen Thread aus
        /// --> dies führt dann in der Event-Behandlung zu einem Problem wenn dort
        /// die Zugriffe auf das UI nicht in den UI-Thread gemarshalled werden.
        /// </summary>
        public void OnTestWithoutSynchronization()
        {
            new Thread(new ThreadStart(this.OnTestEventUnSynchronized)).Start();
        }
        #endregion

        #region Private Event Invokers - UnSynchronized
        /// <summary>
        /// Dies ist eine normaler unsynchronisierter Event-Invoker
        /// </summary>
        private void OnTestEventUnSynchronized()
        {
            if (this.TestEvent!=null)
            {
                this.TestEvent(this,EventArgs.Empty);
            }
        }
        #endregion

        #region Private Event Invokers - Synchronized
        /// <summary>
        /// Dies ist der Spezielle Event-Invoker welcher den Event durch die
        /// Basisklasse im richtigen Thread (UI) feuern lässt
        /// </summary>
        private void OnTestEventSynchronized()
        {
            base.InvokeEventOnUIThread(this.TestEvent,this,EventArgs.Empty);
        }
        #endregion
    }
}

Und noch der Test-Code:

C#-Code:
        private void sampleComponent1_TestEvent(object sender, System.EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine(this.InvokeRequired);
        }

        private void btnCallSynchronizedEvent_Click(object sender, System.EventArgs e)
        {
            this.sampleComponent1.OnTestInOtherThreadSynchronized();
        }

        private void btnUnsynchronized_Click(object sender, System.EventArgs e)
        {
            this.sampleComponent1.OnTestWithoutSynchronization();
        }

Wie man sieht wird im Debugger false ausgegeben wenn man den Synchronisierten-Button drückt.... anderenfalls true....

Das Marshalling in den UI-Thread wird ab sofort durch die Komponente erledigt.... ein Marshalling im Event-behandelnden Code ist somit nicht mehr nötig.

Programmierhans

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Programmierhans am 15.12.2005 17:21.

15.12.2005 17:07 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegen mehr als 11 Monate.
mutzel
myCSharp.de-Mitglied

Dabei seit: 03.03.2006
Beiträge: 40


mutzel ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

hab mir das beispiel grade angeguckt und ausprobiert .. das problem ist das das gnze nur läuft wenn man noch im designer ist .. sobald man das projekt startet ist der host immmer null... mach ich irgend etwas falsch oder ist das beispiel nur für den designer gedacht ... wenn ja kann man das parent control auch während der laufzeit herausfinden?

C#-Code:
        public override ISite Site
        {
            get
            {
                return base.Site;
            }
            set
            {
                base.Site = value;
                if (this.Site!=null)
                {
                    System.ComponentModel.Design.IDesignerHost host = (System.ComponentModel.Design.IDesignerHost)value.GetService (typeof (System.ComponentModel.Design.IDesignerHost));
                    if (host!=null)      //    <-  Host ist zur laufzeit null
                    {
                        Control ctrlParent = host.RootComponent as Control;
                        if (ctrlParent!=null)
                        {
                            this._SynchronizingObject=ctrlParent;
                        }
                    }
                }
            }
        }

lg mutzel
16.11.2006 12:31 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Golo Roden Golo Roden ist männlich
myCSharp.de-Mitglied

avatar-2167.png


Dabei seit: 04.10.2003
Beiträge: 4.207
Entwicklungsumgebung: Visual Studio 2010
Herkunft: Riegel am Kaiserstuhl


Golo Roden ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
Original von Programmierhans
Zugriffe auf das UI sind nur aus dem UI-Thread erlaubt......

Um nun nicht bei jedem Event überprüfen zu müssen (mittels this.InvokeRequired)

Muss man ja auch nicht ... einfach in jede UI-Methode einen MethodInvoker rein, der den eigentlichen Code als anonymen Delegate aufruft und gut ist.
16.11.2006 12:50 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Programmierhans
myCSharp.de-Poweruser/ Experte

avatar-1651.gif


Dabei seit: 05.04.2005
Beiträge: 4.221
Entwicklungsumgebung: VS2003-VS2013 / SAP WebIDE
Herkunft: Zentralschweiz

Themenstarter Thema begonnen von Programmierhans

Programmierhans ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
Original von Golo

Muss man ja auch nicht ... einfach in jede UI-Methode einen MethodInvoker rein, der den eigentlichen Code als anonymen Delegate aufruft und gut ist.

Gibts erst ab VS 2005 Augenzwinkern
16.11.2006 13:24 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
mutzel
myCSharp.de-Mitglied

Dabei seit: 03.03.2006
Beiträge: 40


mutzel ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
Original von Golo
Muss man ja auch nicht ... einfach in jede UI-Methode einen MethodInvoker rein, der den eigentlichen Code als anonymen Delegate aufruft und gut ist.

dann müsste man ja das control verändern und die methoden würden auch über ein delegate aufgerufen werden auch wenn das garnicht nötig ist

ich will das invoke aber in meiner component machen und nicht erst im control .. so das die methode des controls beim klick auf einen button ohne invoke auskommt und beim zugriff von der multithread component per invoke gestartet wird ..


.. mal ganz davon abgesehen das ein invoke mit einem anonymen delegate garnich geht (glaube ich zumindest) EDIT: ok hab mich geirrt


lg martin

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von mutzel am 16.11.2006 13:34.

16.11.2006 13:30 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Golo Roden Golo Roden ist männlich
myCSharp.de-Mitglied

avatar-2167.png


Dabei seit: 04.10.2003
Beiträge: 4.207
Entwicklungsumgebung: Visual Studio 2010
Herkunft: Riegel am Kaiserstuhl


Golo Roden ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Dann kannst Du das Control aber auch einfach verwenden, ohne Dir Gedanken drum machen zu müssen, ob da nun ein InvokeRequired hin muss oder nicht, da das Control an sich entweder eben threadsafe ist oder eben nicht. Und das ist IMHO die Verantwortlichkeit dessen, der das Control entwickelt.

Invoke und anonyme Delegates schließen sich nicht gegenseitig aus.
16.11.2006 13:32 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 14 Jahre.
Der letzte Beitrag ist älter als 13 Jahre.
Antwort erstellen


© Copyright 2003-2020 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 12.08.2020 20:57