none
Threadvarianten RRS feed

  • Frage

  • Hallo,
    nochmals ein paar Fragen zu Threads.
    Der Link ist recht gut, dennoch ist noch was unklar.       Beispiele

    Ich habe eine Applikation -> mehrere Thread, somit reicht lock.
    Was unklar ist.
       Wann ist es sinnvoll so zu machen ?   readonly object _locker = new object();
       Wann ist es sinnvoll so zu machen ?   lock(this)
       ------------------------------------------------------
    Es gibt n Möglichkeiten einen Thread aufzusetzen.
    Das Beispiel ist für mich sinnig, leicht zu verstehen, dennoch gibt es beim Konstruktor wieder n Möglichkeiten.
        new Thread(new ParameterizedThreadStar....
      also zweimal new
    Wann ist was sinnvoll, das wäre die Frage.
        -------------------------------------------------------
    BackgroundThread
       Hier stellte ich gest, dass jeder Aufruf von RunWorkerAsync eine neue ThreadID anlegt,
       somit bin ich zum ProducerConsumerQueue Beispiel übergegangen, da mir das Probleme bereitet.
       Könnt ihr das bestätigen, dass wäre meine Frage.

     Das war es.

     Viele Grüße Sandra

    using System;
    using System.Threading;
    using System.Collections.Generic;
     
    class ProducerConsumerQueue : IDisposable
    {
      EventWaitHandle _wh = new AutoResetEvent (false);
      Thread _worker;
      readonly object _locker = new object();
      Queue<string> _tasks = new Queue<string>();
     
      public ProducerConsumerQueue()
      {
        _worker = new Thread (Work);
        _worker.Start();
      }
     
      public void EnqueueTask (string task)
      {
        lock (_locker) _tasks.Enqueue (task);
        _wh.Set();
      }
     
      public void Dispose()
      {
        EnqueueTask (null);     // Signal the consumer to exit.
        _worker.Join();         // Wait for the consumer's thread to finish.
        _wh.Close();            // Release any OS resources.
      }
     
      void Work()
      {
        while (true)
        {
          string task = null;
          lock (_locker)
            if (_tasks.Count > 0)
            {
              task = _tasks.Dequeue();
              if (task == null) return;
            }
          if (task != null)
          {
            Console.WriteLine ("Performing task: " + task);
            Thread.Sleep (1000);  // simulate work...
          }
          else
            _wh.WaitOne();         // No more tasks - wait for a signal
        }
      }
    }
    
    
    
    
    static void Main()
    {
    _bw = new BackgroundWorker
    {
      WorkerReportsProgress = true,
      WorkerSupportsCancellation = true
    };
    _bw.DoWork += bw_DoWork;
    _bw.ProgressChanged += bw_ProgressChanged;
    _bw.RunWorkerCompleted += bw_RunWorkerCompleted;
    
    _bw.RunWorkerAsync ("Hello to worker");
    	
    	
    	
    public clsProt()
    {
    	Thread th = new Thread(new ParameterizedThreadStart(DoWork)) { IsBackground = true };
    	//Thread th = new Thread(new ThreadStart(DoWork)) { IsBackground = true };
    
    	//clsProt w = new clsProt();
    	//Thread th = new Thread(w.DoWork);
    	th.Start();
    }
    
    
    lock (Monitor.Enter / Monitor.Exit) Ensures just one thread can access a resource,
                                        or section of code at a time - 20ns 
    Mutex  Yes 1000ns 
    
    void WorkThreadTelegrams()
    {
    	while (true)
    	{
    		EvNewStep.WaitOne();
    
    		if (ExitThreadStepSequence)
    			return;
    
    		lock (_LockerThreadStepSequence)
    			DistributeMessagesDependingType();
    	}
    }
    

        
    Mittwoch, 18. Mai 2016 16:32

Antworten

  • Hallo Sandra,

    sich einfach Code abzukupfern und zu hoffen, das hat der andere schon richtig gemacht, funktioniert in den seltesten Fällen. Deswegen wiederholt: Lies bitte auch die Dokumentation. Hier wäre es der Thread Pool, auf den ich bereits (mehrmalig) hingewiesen habe.

    Wenn ich hier einen Dummy BackgroundWorker starte, so verwendet er mal den einen mal den anderen Thread  - in etwa 60 Sekunden wurden insgesamt 3 Threads wechselnd benutzt.

    Denn es ist nicht zwangsläufig so, das ein neuer Thread verwendet wird. Vielmehr versucht der Thread Pool freie Threads wieder zu verwenden.

    Was zum einen voraussetzt, er erkennt das der Thread frei ist, was nicht immer sofort der Fall ist. Zudem kann es andere Programmteile geben, die Threads benutzen, ist nicht gesagt, dass immer genau der gleiche ist.

    Gruß Elmar

    • Als Antwort markiert Sandra Bauer Donnerstag, 19. Mai 2016 16:47
    Donnerstag, 19. Mai 2016 08:44
    Beantworter
  • Hallo Sandra,

    aus RunWorkerAsync kann man nichts schließen, auch wenn Florian da was sehen möchte. Nur ist da nichts, weil die Verarbeitung durch den Thread Pool durchgeführt wird, denn RunWorkerAsync bemüht BeginInvoke für den Delegaten. Mehr dazu siehe:

    Asynchrone Programmierung mithilfe von Delegaten.

    Die Regeln für Sperren via Monitor / C# lock sind allgemein und gelten auch im anderen "Beispiel". _LockerThreadStepSequence wäre eine private Object-Variable (mit einem "bescheuerten" weil irreführenden Namen :).

    Gruß Elmar

    • Als Antwort markiert Sandra Bauer Montag, 23. Mai 2016 17:02
    Samstag, 21. Mai 2016 11:47
    Beantworter
  • Hallo Sandra,

    Falls Du sicherstellen wolltest das genau ein Thread verwendet wird, dann kannst Du diesen Thread einfach selbst anlegen.

    ...

    ja genau so mache ich es jetzt.

     Interessant wäre wie Du das machst. 

    public ProducerConsumerQueue()
      {
        _worker = new Thread (Work);
        _worker.Start();
      }

    Es gibt halt viele Varianten (im Konstruktur)  und der Sinn ist mir nicht ganz klar.

    Prinzipiell geht es ja jetzt und möchte hier auch nicht weiter bohren. Passt schon.

    Grüße Sandra

    Ich verwende unterschiedliche Varianten, je nach Bedarf - durchaus auch deinem Beispiel ähnlich. So kann ich mir vorstellen, dass die von dir genannte Variante reicht.
    Um es schneller runterzuschreiben auch gerne mit Lambda. In meinem letzten Anwendungsfall war zusätzlich der Apartmentstate relevant und musste zwingend gesetzt werden, außerdem benötigte ich einen Synchonisationskontext und einige Eventhandler. Das Anlegen des Threads sah ungefähr so aus:

    Thread meinThread = new Thread(() => meineMethode(param1, param2, param3));
    			meinThread.IsBackground = true;
    			meinThread.SetApartmentState(ApartmentState.STA);
    			meinThread.Start();

    Die Gründe für die oben genannte Variante sind einigermaßen speziell. Denkbar wäre auch das Rückgabewerte interessant sind.

    Gruß


    - Florian

    • Als Antwort markiert Sandra Bauer Donnerstag, 26. Mai 2016 11:54
    Dienstag, 24. Mai 2016 07:33

Alle Antworten

  • Hallo Sandra,

    endlos oft beantwortet, wenn auch häufiger in englisch, u. a. :

    Why is lock(this) {…} bad?

    Einiges findet sich bereits bei der Lock Anweisung.

    Bitte studieren, denn solche Frage braucht man nicht noch tausend Mal.

    Gruß Elmar

    Mittwoch, 18. Mai 2016 16:43
    Beantworter
  • Bitte studieren, denn solche Frage braucht man nicht noch tausend Mal.

    Hallo Elmar,

    ja, Praxisbeispiele sind halt am besten;-)

    BackgroundWorker -- Hier stellte ich fest, dass jeder Aufruf von RunWorkerAsync eine neue ThreadID anlegt.

    Kannst hier noch kurz was sagen. Warum ist das so.

    Ich suche meist Anwendungsfall, dann am besten dieser Lösungsweg. Durch die Vielzahl der Möglichkeiten wir man erschlagen. Danke im Voraus.

    Grüße Sandra

    Mittwoch, 18. Mai 2016 17:00
  • Hallo Sandra,

    die Antwort warum es immer eine neue ThreadID gibt steht in der Dokumentation:
    RunWorkerAsync

    Solange dein BackgroundWorker noch läuft, sollte es eine Exception geben.

    Gruß


    - Florian

    Donnerstag, 19. Mai 2016 08:04
  • Hallo Sandra,

    sich einfach Code abzukupfern und zu hoffen, das hat der andere schon richtig gemacht, funktioniert in den seltesten Fällen. Deswegen wiederholt: Lies bitte auch die Dokumentation. Hier wäre es der Thread Pool, auf den ich bereits (mehrmalig) hingewiesen habe.

    Wenn ich hier einen Dummy BackgroundWorker starte, so verwendet er mal den einen mal den anderen Thread  - in etwa 60 Sekunden wurden insgesamt 3 Threads wechselnd benutzt.

    Denn es ist nicht zwangsläufig so, das ein neuer Thread verwendet wird. Vielmehr versucht der Thread Pool freie Threads wieder zu verwenden.

    Was zum einen voraussetzt, er erkennt das der Thread frei ist, was nicht immer sofort der Fall ist. Zudem kann es andere Programmteile geben, die Threads benutzen, ist nicht gesagt, dass immer genau der gleiche ist.

    Gruß Elmar

    • Als Antwort markiert Sandra Bauer Donnerstag, 19. Mai 2016 16:47
    Donnerstag, 19. Mai 2016 08:44
    Beantworter
  • die Antwort warum es immer eine neue ThreadID gibt steht in der Dokumentation:
    RunWorkerAsync

    Hallo Florian,

      dass bei jedem RunWorkerAsync eine neue ID entsteht, ist so, aber ich lese es aus Deinem Link heraus.

    Grüße Sandra

    Donnerstag, 19. Mai 2016 16:49
  • Hallo Sandra,

    die Antwort warum es immer eine neue ThreadID gibt steht in der Dokumentation:
    RunWorkerAsync

    Hallo Florian,

      dass bei jedem RunWorkerAsync eine neue ID entsteht, ist so, aber ich lese es aus Deinem Link heraus.

    das ist wunderbar. Es steht dort auch direkt im ersten Satz "Startet die Ausführung eines Hintergrundvorgangs.".

    Gruß


    - Florian


    Freitag, 20. Mai 2016 06:32
  • Hallo Florian,
    ich will wirklich das Thema nicht aufheizen.
    So steht es da, wenn man daraus eine neue ID schließen kann, ok.https://msdn.microsoft.com/library/h01xszh2(v=vs.110).aspx
    ################################
    Hinweise
    --------------------------------------------------------------------------------
    Die RunWorkerAsync-Methode fordert die asynchrone Ausführung des Vorgangs an.Bei der Verarbeitung der Anforderung wird das DoWork-Ereignis ausgelöst, welches dann seinerseits die Ausführung des Hintergrundvorgangs startet.
    Wenn der Hintergrundvorgang bereits ausgeführt wird, löst der erneute Aufruf von RunWorkerAsync eine InvalidOperationException aus.
    ################################
    Elmar hat natürlich recht.
    Vermeiden Sie es grundsätzlich, einen public-Typ zu sperren oder Instanzen, die nicht durch Ihren Code gesteuert werden.
    Die allgemeinen Konstrukte lock (this), lock (typeof (MyType)) und lock ("myLock") verstoßen gegen diese Richtlinie: 

    ################################

    public static readonly Object myLock = new Object();
    lock (myLock)
    {
    	MyToDos();
    }
    Dann ist das doch besser.

    Teils wird auch so was gemacht. In einer Funktion einen Teil.
    Da wäre meine Bitte gewesen, welche Kriterien gibt es für diese Lösung.

    void WorkThreadTelegrams()
            {
                while (true)
                {
                    string task = null;
                    lock (_LockerThreadStepSequence)
                        if (Queue.Count > 0)
                        {
                            task = Queue.Take();
                            if (task == null)
                                return;
                        }
    
                    if (task != null)
                    {
                        DistributeSomething(task);
                        EvNewStep.Reset();
                    }

    Viele Grüße Sandra


    Freitag, 20. Mai 2016 15:20
  • Hallo Sandra,

    aus RunWorkerAsync kann man nichts schließen, auch wenn Florian da was sehen möchte. Nur ist da nichts, weil die Verarbeitung durch den Thread Pool durchgeführt wird, denn RunWorkerAsync bemüht BeginInvoke für den Delegaten. Mehr dazu siehe:

    Asynchrone Programmierung mithilfe von Delegaten.

    Die Regeln für Sperren via Monitor / C# lock sind allgemein und gelten auch im anderen "Beispiel". _LockerThreadStepSequence wäre eine private Object-Variable (mit einem "bescheuerten" weil irreführenden Namen :).

    Gruß Elmar

    • Als Antwort markiert Sandra Bauer Montag, 23. Mai 2016 17:02
    Samstag, 21. Mai 2016 11:47
    Beantworter
  • Hallo Sandra

    So steht es da, wenn man daraus eine neue ID schließen kann, ok.

    Du kannst daraus schließen das dir nicht die Verwendung eines bestimmten Threads garantiert wird (Im Detail ist es einer aus dem ThreadPool). In weiteren Teilen der Dokumentation ist dies auch weiter beschrieben und Elmar hat das ja im Detail (mit entsprechenden Verweisen) schon erläutert.

    Falls Du sicherstellen wolltest das genau ein Thread verwendet wird, dann kannst Du diesen Thread einfach selbst anlegen.

    Gruß


    - Florian

    Montag, 23. Mai 2016 07:05
  • Falls Du sicherstellen wolltest das genau ein Thread verwendet wird, dann kannst Du diesen Thread einfach selbst anlegen.

    Hallo Florian,

    ja genau so mache ich es jetzt.

     Interessant wäre wie Du das machst.

       So ähnlich

        
    public ProducerConsumerQueue()
      {
        _worker = new Thread (Work);
        _worker.Start();
      }

    Es gibt halt viele Varianten (im Konstruktur)  und der Sinn ist mir nicht ganz klar.

    Prinzipiell geht es ja jetzt und möchte hier auch nicht weiter bohren. Passt schon.

    Grüße Sandra

    Montag, 23. Mai 2016 17:04
  • Hallo Sandra,

    auch dem dafür gibt es einen Artikel in der Dokumentation:

    Erstellen von Threads und Übergeben von Daten zur Startzeit

    Wiederholt: "Selbermachen" ist nicht automatisch besser, schon gar nicht, wenn man bereits bei den Anfängen grübelt.

    Gruß Elmar

    Dienstag, 24. Mai 2016 06:31
    Beantworter
  • Hallo Sandra,

    Falls Du sicherstellen wolltest das genau ein Thread verwendet wird, dann kannst Du diesen Thread einfach selbst anlegen.

    ...

    ja genau so mache ich es jetzt.

     Interessant wäre wie Du das machst. 

    public ProducerConsumerQueue()
      {
        _worker = new Thread (Work);
        _worker.Start();
      }

    Es gibt halt viele Varianten (im Konstruktur)  und der Sinn ist mir nicht ganz klar.

    Prinzipiell geht es ja jetzt und möchte hier auch nicht weiter bohren. Passt schon.

    Grüße Sandra

    Ich verwende unterschiedliche Varianten, je nach Bedarf - durchaus auch deinem Beispiel ähnlich. So kann ich mir vorstellen, dass die von dir genannte Variante reicht.
    Um es schneller runterzuschreiben auch gerne mit Lambda. In meinem letzten Anwendungsfall war zusätzlich der Apartmentstate relevant und musste zwingend gesetzt werden, außerdem benötigte ich einen Synchonisationskontext und einige Eventhandler. Das Anlegen des Threads sah ungefähr so aus:

    Thread meinThread = new Thread(() => meineMethode(param1, param2, param3));
    			meinThread.IsBackground = true;
    			meinThread.SetApartmentState(ApartmentState.STA);
    			meinThread.Start();

    Die Gründe für die oben genannte Variante sind einigermaßen speziell. Denkbar wäre auch das Rückgabewerte interessant sind.

    Gruß


    - Florian

    • Als Antwort markiert Sandra Bauer Donnerstag, 26. Mai 2016 11:54
    Dienstag, 24. Mai 2016 07:33