codestory

Die Anleitung zu C# Multithreading Programmierung

  1. Der Begriff von Multithreading
  2. Den Parameter ins Thread übertragen
  3. Thread benutzt die nicht-static Methode
  4. ThreadStart Delegate
  5. Thread mit den anonymen Kode
  6. Den Name für Thread stellen
  7. Die Priorität zwischen Thread
  8.  Join() benutzen
  9.  Yield() benutzen

1. Der Begriff von Multithreading

Multi Thread ist ein wichtiger Begriff in der Programmiersprache und so ist die C#. Das ist die Weise der Erstellung der parallelen Thread. Sehen Sie bitte ein folgendes Beispiel um mehr zu verstehen:
HelloThread.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MultithreadingTutorial
{
    class HelloThread
    {
        public static void Main(string[] args)
        { 
            Console.WriteLine("Create new Thread...\n");   
            // Ein Sub-Thread erstellen um parallel mit dem Haupt-Thread (main thread) zu laufen.
            Thread newThread = new Thread(WriteB); 
            Console.WriteLine("Start newThread...\n");

            // Das Laufen vom newThread aktivieren
            newThread.Start(); 
            Console.WriteLine("Call Write('-') in main Thread...\n");

            // Im Haupt-Thread schreiben die Zeichen  '-'
            for (int i = 0; i < 50; i++)
            {
                Console.Write('-'); 
                // 70 milisekunde schlafen (sleep).
                Thread.Sleep(70);
            } 
            Console.WriteLine("Main Thread finished!\n");
            Console.Read();
        }  
        public static void WriteB()
        {
            // Die 100 Mal Schleife schreibt das Zeichen 'B' auf
            for (int i = 0; i < 100; i++)
            {
                Console.Write('B');

                // 100 milisekunde schlafen
                Thread.Sleep(100);
            } 
        }
    }  
}
Und dann starten Sie die Klasse
Das Arbeitsprinzip des Thread wird im folgenden Beispiel erläutert:

2. Den Parameter ins Thread übertragen

Im obenen Part haben Sie mit dem Beispiel über HelloThread kennengelernt. Sie erstellten ein Objekt, das eine static Methode einpackt um diese Methode gleichzeitig mit dem Vater Thread durchzuführen
Die static Methode kann einen Parameter zur Übertragung in dem Constructor der Klasse Thread werden wenn die Methode keinen Parameter oder einen einzigen Parameter Objekt hat.
// Eine static Methode, keinen Parameter.
public static void LetGo()
{
      // ...
}

// Die static Method hat einen einzigen Parameter und das Typ von Objekt
public static void GetGo(object value)
{
      // ...
}
Im nächsten Beispiel erstelle ich ein Thread, der eine static Methode mit einen Parameter (Objekt typ) einpackt. Ich laufe das Thread und übertrage die Wert für den Parameter .
MyWork.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MultithreadingTutorial
{
    class MyWork
    { 
        public static void DoWork(object ch)
        {
            for (int i = 0; i < 100; i++)
            {  
                Console.Write(ch); 
                // 50 milisekunde schlafen
                Thread.Sleep(50);
            }
        } 
    }
}
ThreadParamDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MultithreadingTutorial
{
    class ThreadParamDemo
    { 
        public static void Main(string[] args)
        {  
            Console.WriteLine("Create new thread.. \n");

            // Ein Objekt Thread packend (wrap) die statice Methode MyWork.DoWork erstellen
            Thread workThread = new Thread(MyWork.DoWork); 
            Console.WriteLine("Start workThread...\n");

            // workThread laufen und in dem Parameter für die Methode  MyWork.DoWork übertragen.
            workThread.Start("*"); 

            for (int i = 0; i < 20; i++)
            {
                Console.Write("."); 
                // 30 Sekunde schlafen
                Thread.Sleep(30);
            } 
            Console.WriteLine("MainThread ends");
            Console.Read();
        }
    } 
}
Die Klasse ThreadParamDemo:starten

3. Thread benutzt die nicht-static Methode

Sie können auch einen Thread durch die Benutzung der normalen Methoden. Sehen Sie das Beispiel:
Worker.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace MultithreadingTutorial
{
    class Worker
    {
        private string name;
        private int loop;
        public Worker(string name, int loop)
        {
            this.name = name;
            this.loop = loop;
        }
        public void DoWork(object value)
        {
            for (int i = 0; i < loop; i++)
            {
                Console.WriteLine(name + " working " + value);
                Thread.Sleep(50);
            }
        }
    }
}
WorkerTest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MultithreadingTutorial
{
    class WorkerTest
    { 
        public static void Main(string[] args)
        {
            Worker w1 = new Worker("Tom",10);

            // Das Objekt Thread erstellen.
            Thread workerThread1 = new Thread(w1.DoWork);

            // Den Parameter in die Methode DoWork übertragen.
            workerThread1.Start("A"); 

            Worker w2 = new Worker("Jerry",15);

            // Das Objekt Thread erstellen.
            Thread workerThread2 = new Thread(w2.DoWork);

            // Den Parameter in die Methode DoWork übertragen.
            workerThread2.Start("B");  
            Console.Read();
        }
    } 
}
Das Beispiel durchführen:
Tom working A
Jerry working B
Jerry working B
Tom working A
Tom working A
Jerry working B
Tom working A
Jerry working B
Tom working A
Jerry working B
Tom working A
Jerry working B
Tom working A
Jerry working B
Jerry working B
Tom working A
Tom working A
Jerry working B
Jerry working B
Tom working A
Jerry working B
Jerry working B
Jerry working B
Jerry working B
Jerry working B

4. ThreadStart Delegate

ThreadStart ist eine Beauftragungsklasse (Delegate), sie wird durch die Einpackung einer Methode erstellt. Und sie wird wie ein Parameter zur Erstellung eines Objekt Thread übertragen
Um einen Thread im .Net < 2.0 zu starten, sollen Sie ein ThreadStart erstellen. Das ist eine delegate.

Ab der Version 2.0 von .NET Framework ist es nicht notwendig, ein ThreadStart klar zu erstellen. Sie brauchen nur den Namen der Methode im Constructor des Thread bestimmen
Programmer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MultithreadingTutorial
{
    class Programmer
    {
        private string name;
        public Programmer(string name)  
        {
            this.name= name;
        } 
        // Das ist eine unstatic Methode, die keinen Parameter hat 
        public void DoCode()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine(name + " coding ... ");
                Thread.Sleep(50);
            } 
        }
    } 
}
ThreadStartDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;


namespace MultithreadingTutorial
{
    class ThreadStartDemo
    { 
        public static void Main(string[] args)
        {
            // Das Objekt ThreadStart packend eine static Methode erstellen
            // (Es kann die Methode ohne Parameter packen
            // (Es ist das Objekt zur Durchführung der Methode).
            ThreadStart threadStart1 = new ThreadStart(DoWork);

            // Ein Thread packend (wrap) threadStart1 erstellen.
            Thread workThread = new Thread(threadStart1);

            // Die Aufruf auf  start thread
            workThread.Start();

            // Ein Objekt Programmer initializieren
            Programmer tran = new Programmer("Tran");

            // Sie können das Objekt ThreadStart erstellen um die statice Methode einzupacken .
            // (ThreadStart kann die Methode ohne Parameter einpacken)
            ThreadStart threadStart2 = new ThreadStart(tran.DoCode);

            // Ein Thread packend threadStart2 erstellen.
            Thread progThread = new Thread(threadStart2); 
            progThread.Start();

            Console.WriteLine("Main thread ends");
            Console.Read();
        } 
        public static void DoWork()
        {
            for (int i = 0; i < 10; i++)
            {
                Console.Write("*");
                Thread.Sleep(100);
            }                
        }
    } 
}
Das Beispeil durchführen:
Main thread ends
*Tran coding ...
Tran coding ...
Tran coding ...
*Tran coding ...
*Tran coding ...
*******

5. Thread mit den anonymen Kode

Im den obengenannten Part haben Sie die Thread durch die Benutzung einer bestimmten Methode erstellt. Sie können einen Thread erstellen um irgendeine Code zu implementieren.
// delegate() benutzen um eine anonyme Methode zu erstellen
delegate()
{
     //  ...
}
Zum Beispiel
ThreadUsingSnippetCode.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MultithreadingTutorial
{
    class ThreadUsingSnippetCode
    { 
        public static void Main(string[] args) 
        { 
            Console.WriteLine("Create thread 1"); 
            // Ein Thread erstellen um eine Code durchzuführen
            Thread newThread1 = new Thread(
                delegate()
                {
                    for (int i = 0; i < 10; i++)
                    {
                        Console.WriteLine("Code in delegate() " + i);
                        Thread.Sleep(50);
                    } 
                }
            ); 
            Console.WriteLine("Start newThread1");  
            // Start thread. 
            newThread1.Start(); 
            Console.WriteLine("Create thread 2");

            // Ein Thread erstellen um eine Code durchzuführen.
            Thread newThread2 = new Thread(
                delegate(object value)
                {
                    for (int i = 0; i < 10; i++)
                    {
                        Console.WriteLine("Code in delegate(object) " + i + " - " + value);
                        Thread.Sleep(100);
                    } 
                }
            ); 
            Console.WriteLine("Start newThread2");

            // Thread 2 beginnen
            // Die Wert für delegate() übertragen.
            newThread2.Start("!!!");  
            Console.WriteLine("Main thread ends");
            Console.Read(); 
        }
    } 
}
Das Beispiel durchführen
Create thread 1
Start newThread1
Create thread 2
Start newThread2
Main thread ends
Code in delegate() 0
Code in delegate(object) 0 - !!!
Code in delegate() 1
Code in delegate() 2
Code in delegate(object) 1 - !!!
Code in delegate() 3
Code in delegate(object) 2 - !!!
Code in delegate() 4
Code in delegate() 5
Code in delegate(object) 3 - !!!
Code in delegate() 6
Code in delegate() 7
Code in delegate(object) 4 - !!!
Code in delegate() 8
Code in delegate() 9
Code in delegate(object) 5 - !!!
Code in delegate(object) 6 - !!!
Code in delegate(object) 7 - !!!
Code in delegate(object) 8 - !!!
Code in delegate(object) 9 - !!!

6. Den Name für Thread stellen

In der Programmierung des MultiThread können Sie automatisch den Name für den Thread stellen. Es ist wirklich nützlich beim Debugging. Damit können wir kennen, die Code wird in welchem Thread implementiert.

In einem Thread können Sie Thread.CurrentThread.Name aufrufen um den Name des implementierenden Thread zu haben
Sehen Sie das Beispiel
NamingThreadDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MultithreadingTutorial
{
    class NamingThreadDemo
    { 
        public static void Main(string[] args)
        {
            // Den Name für das aktuelle Thread stellen (Das Hauptthread)
            Thread.CurrentThread.Name = "Main"; 
            Console.WriteLine("Code of "+ Thread.CurrentThread.Name); 
            Console.WriteLine("Create new thread");

            // Ein Thread erstellen.
            Thread letgoThread = new Thread(LetGo);

            // Den Name für das Thread stellen
            letgoThread.Name = "Let's Go"; 
            letgoThread.Start();

            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine("Code of " + Thread.CurrentThread.Name);
                Thread.Sleep(30);
            } 
            Console.Read();
        } 
        public static void LetGo()
        {
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine("Code of " + Thread.CurrentThread.Name);
                Thread.Sleep(50);
            }
        }
    } 
}
Das Beispiel durchführen
Code of Main
Create new thread
Code of Main
Code of Let's Go
Code of Main
Code of Let's Go
Code of Main
Code of Main
Code of Main
Code of Let's Go
Code of Let's Go
Code of Let's Go
Code of Let's Go
Code of Let's Go
Code of Let's Go
Code of Let's Go
Code of Let's Go

7. Die Priorität zwischen Thread

Im C# gibt es 5 Prioritätlevel eines Thread. Sie werden in enum ThreadPriority.definiert
** ThreadPriority enum **
enum ThreadPriority {
    Lowest,
    BelowNormal,
    Normal,
    AboveNormal,
    Highest
}
Normalerweise ist es Ihnen beim der highspeed Computer schwierig, den Unterschied zwischen der hochprioritäten Thread und der wenigprioritäten Thread zu sehen wenn die Thread wenige Arbeit machen.

Das folgende BEispiel hat 2 Thread. Jeder Thread drückt 100K Textzeile aus. (Eine genug große Menge zeigt den Unterschied).
ThreadPriorityDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MultithreadingTutorial
{
    class ThreadPriorityDemo
    {
        private static DateTime endsDateTime1;
        private static DateTime endsDateTime2; 
        public static void Main(string[] args)
        {
            endsDateTime1 = DateTime.Now;
            endsDateTime2 = DateTime.Now;

            Thread thread1 = new Thread(Hello1);

            // Die höchste Priorität für thread1 stellen
            thread1.Priority = ThreadPriority.Highest; 
            Thread thread2 = new Thread(Hello2);

            // Die niedrigste Prioriät für thread2 stellen
            thread2.Priority = ThreadPriority.Lowest; 
            thread2.Start(); thread1.Start(); 
            Console.Read();
        }  
        public static void Hello1()
        {
            for (int i = 0; i < 100000; i++)
            {
                Console.WriteLine("Hello from thread 1: "+ i);
            }
            // Die Zeit der Ende des thread1 .
            endsDateTime1 = DateTime.Now; 
            PrintInterval();
        } 
        public static void Hello2()
        {
            for (int i = 0; i < 100000; i++)
            {
                Console.WriteLine("Hello from thread 2: "+ i);
            }
            // Die Zeit der Ende des thread2 .
            endsDateTime2 = DateTime.Now; 
            PrintInterval();
        } 
        private static void PrintInterval()
        {
            // Der Zeitraum (Milisekunde)
            TimeSpan interval = endsDateTime2 - endsDateTime1; 
            Console.WriteLine("Thread2 - Thread1 = " + interval.TotalMilliseconds + " milliseconds");
        }
    } 
}
Das Beispiel durchführen

8.  Join() benutzen

Thread.Join() ist eine Methode zu informieren, dass wart auf die Erledigung eines Thread und dann der VaterThread weiter läuft.
ThreadJoinDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;


namespace MultithreadingTutorial
{
    class ThreadJoinDemo
    { 
        public static void Main(string[] args)
        {
            Console.WriteLine("Create new thread"); 
            Thread letgoThread = new Thread(LetGo); 
            
            // Ein Thread beginnen (start thread).
            letgoThread.Start();

            // Mit dem VaterThread sagen (hier ist das Main Thread)
            // Warten auf die Erledigung vom letgoThread und dann laufen weiter.
            letgoThread.Join();
            
            // Die Code wartet auf die Erledigung von letgoThread und dann läuft weiter
            Console.WriteLine("Main thread ends");
            Console.Read();
        }  
        public static void LetGo()
        {
            for (int i = 0; i < 15; i++)
            {
                Console.WriteLine("Let's Go " + i);
            }
        }
    } 
}
Das Beispiel durchführen
Create new thread
Let's Go 0
Let's Go 1
Let's Go 2
Let's Go 3
Let's Go 4
Let's Go 5
Let's Go 6
Let's Go 7
Let's Go 8
Let's Go 9
Let's Go 10
Let's Go 11
Let's Go 12
Let's Go 13
Let's Go 14
Main thread ends

9.  Yield() benutzen

Theoretisch bedeutet "Yield" Übergabe, Verlassung, Kapitulation. Ein Thread Yield sagt dem Betriebssystem, dass er lasst die anderen Thread in seiner Postion liegen. Das zeigt, dass er ist nicht etwas wichtig. Beachten Sie, dass er ist nur eine Vorschlage, obwohl er ist nicht sicher, auf alles zu wirken
Deshalb wird die Methode Yield() benutzt wenn er ist nicht beschäftig, er muss etwas nicht wichtig machen so er schlägt vor, dass das Betriebssystem vorläufig die Priorität für anderen Thread gibt.
Das folgende Beispiel, Es gibt 2 Thread, jeder Thread drückt 100K Textzeile ( genug groß sein, den Unterschied zu zeigen). Ein Thread ist höchstpriorität und ein kleinstpriorität. Wir messen die Zeit zum Stoppen des 2 Thread.
ThreadYieldDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MultithreadingTutorial
{
    class ThreadYieldDemo
    {

        private static DateTime importantEndTime;
        private static DateTime unImportantEndTime;

        public static void Main(string[] args)
        {
            importantEndTime = DateTime.Now;
            unImportantEndTime = DateTime.Now;

            Console.WriteLine("Create thread 1");

            Thread importantThread = new Thread(ImportantWork);

            // Die höchste Priorität für das Thread stellen.
            importantThread.Priority = ThreadPriority.Highest; 

            Console.WriteLine("Create thread 2");

            Thread unImportantThread = new Thread(UnImportantWork);

            // Die niedrigste Priorität für das Thread stellen
            unImportantThread.Priority = ThreadPriority.Lowest; 

            // Start threads. 
            unImportantThread.Start();
            importantThread.Start();
           

            Console.Read();

        }

        // Eine wichtige Dinge fordert die hohe Priorität an.
        public static void ImportantWork()
        {
            for (int i = 0; i < 100000; i++)
            {
                Console.WriteLine("\n Important work " + i); 

                // Dem Betriebssystem informieren, das Thread gibt die Priorität für das andere Thread 
                Thread.Yield();
            }
            // Die Zeitpunkt des Ende des thread.
            importantEndTime = DateTime.Now;
            PrintTime();
        }

        public static void UnImportantWork()
        {
            for (int i = 0; i < 100000; i++)
            {
                Console.WriteLine("\n  -- UnImportant work " + i); 
            }
            // Die Zeitpunkt des Ende des thread.
            unImportantEndTime = DateTime.Now;
            PrintTime();
        }

        private static void PrintTime()
        { 
            // Die Zeitraum (Milisekunde)
            TimeSpan interval = unImportantEndTime - importantEndTime; 
      
            Console.WriteLine("UnImportant Thread - Important Thread = " + interval.TotalMilliseconds +" milliseconds");
        }
         
    }

}
Starten Sie die oben Klasse ohne Thread.Yield():
Starten Sie die oben Klasse falls der höherpriorität Thread ununterbrochen auf Thread.Yield() aufruft um anzufordern, dass das System vorläufig die Priorität für die anderen Thread gibt.