Advanced   Java   Services Thread, ThreadStart und ParameterizedThreadStart Back Next Up Home


Grundlagen

Threads, auch nebenläufige Prozesse genannt, ermöglichen vereinfacht gesagt, Multitasking innerhalb eines Programms. Bestimmte Programmabschnitte kann der Entwickler (quasi-)parallel ablaufen lasssen. C# stellt Mechanismen zur Verzweigung bereit, so daß bestimmte Programmabschnitte parallel (Mehrprozessorsysteme) oder quasiparallel (Einprozessorsysteme) ablaufen können. Für solche Situationen müssen Mechanismen zur Threadkommunikation bereitgestellt werden. Greifen mehrere Threads gleichzeitig auf ein Objekt zu muß man Mechanismen zur Hand haben, um dies gegebenenfalls zu unterbinden um Datenkonsistenz zu gewährleisten. Hierzu gibt es verschiedene Möglichkeiten zur Synchronisation von Threads.


Threadobjekte erzeugen und Thread starten

Threads können vom Entwickler nur eingeschränkt konfiguriert werden, da letzten Endes das Betriebssystem ihren Ablauf bestimmt. Das Betriebssystem ordnet Sie in eine Warteschlange ein, wo sie dann auf Zuteilung von Prozessorzeit warten. Da C# Delegates kennt werden Threads in C# mit Hilfe von Delegates realisiert. Die Klasse Thread verfügt über vier Konsatruktoren. Jeder dieser Konstruktoren erhält ein Delegate.

Konstruktoren
NameBeschreibung
Thread(ThreadStart) Erzeugt eine neue Threadinstanz mit Hilfe eines ThreadStart-Delegates.
Thread(ParameterizedThreadStart) Erzeugt eine neue Threadinstanz mit Hilfe eines ParameterizedThreadStart-Delegates.
Thread(ThreadStart, Int32) Erzeugt eine neue Threadinstanz mit Hilfe eines ThreadStart-Delegates und gibt die maximale Größe für den Stack an.
Thread(ParameterizedThreadStart, Int32) Erzeugt eine neue Threadinstanz mit Hilfe eines ParameterizedThreadStart-Delegates und gibt die maximale Größe für den Stack an.

Die beiden Delegates haben die folgende Form

ThreadStart und ParameterizedThreadStart
public delegate void ThreadStart() Steht für eine Methode, die keinen Parameter bekommt und nichts zurückgibt
public delegate void ParameterizedThreadStart(Object ob) Steht für eine Methode, die einen Parameter vom Typ Object bekommt und nichts zurückgibt

Wenn eine Methode eine dieser beiden Signaturen erfüllt, so kann sie einem Thread übergeben werden und sie wird dann in einem zum aktuellen Thread parallelen Thread abgearbeitet. Zum Starten eines Threads stellt die Klasse Thread die überladene Methode start() zur Verfügung. Soll eine Funktion abgearbeitet werden, die einen Parameter hat, so wird er der Methode Start übergeben.


Erzeugen von Threads mit Methoden der Form void Xxx()
Thread th = new Thread(Xxx);
th.Start();

Erzeugen von Threads mit Methoden der Form void Xxx(Object ob)
Thread th = new Thread(Xxx);
th.Start(ob);

Beispiele

Die folgende Codeschnipsel sind selbsterklärend.









Threadkonstruktor erhält den Namen einer Methode

Abzuarbeitende Methode. Sie wird siebenmal den Namen 'thread' im Abstand von 200 ms ausgeben.

/*
   Die vom Thread verwendete Methode
*/
static void Run()
{
   Thread.CurrentThread.Name = "thread";
   ConsoleColor defaultColor = Console.ForegroundColor;

   for (int i = 0; i < 7; i++)
   {
      Console.ForegroundColor = ConsoleColor.DarkRed;
      Console.WriteLine(Thread.CurrentThread.Name);
      Console.ForegroundColor = defaultColor;
      Thread.Sleep(200);  // 200 ms
   }
}

Erzeugen und Starten des Threads. Main startet den Thread und gibt siebenmal den Namen 'main' aus.

/*
   Der Threadinstanz wird per Konstruktor der Name der Methode übergeben
*/
private static void Main(string[] args)
{
   Thread.CurrentThread.Name = "main";
   Thread th = new Thread(Run);
   th.Start();

   for (int i = 0; i < 7; i++)
   {
      Console.WriteLine(Thread.CurrentThread.Name);
      Thread.Sleep(200);  // 200 ms
   }
}

Hier zwei Abläufe dieses Beispiels. Typisch für Threads ist der nicht genau vorhersagbare Ablauf.

csharp-threads-01.jpg

csharp-threads-02.jpg


Threadkonstruktor erhält einen Lambdaausdruck

public static void Main(string[] args)
{
   Thread.CurrentThread.Name = "main";
   Thread th = new Thread( () =>
   {
      Thread.CurrentThread.Name = "thread";
      for (int i = 0; i <12; i++)
         Console.WriteLine(Thread.CurrentThread.Name + " " + (i + 1));
   });

   th.Start();
   for (int i = 0; i < 4; i++)
      Console.WriteLine(Thread.CurrentThread.Name + " " + (i + 1));
}

Threadkonstruktor erhält eine Methode mit Parameter

Da der Parameter vom Typ Objekt ist, kann der Methode ein beliebiges Objekt übergeben werden. Hier erhält die Methode Call() in Main ein integer-Array.

static void Call(Object ob)
{
   Thread.CurrentThread.Name = "thread";
   ConsoleColor defaultColor = Console.ForegroundColor;

   int[] intArr = (int[])ob;

   for (int i = 0; i < intArr.Length; i++)
   {
      Console.ForegroundColor = ConsoleColor.DarkRed;
      Console.WriteLine(Thread.CurrentThread.Name + " " + intArr[i]);
      Console.ForegroundColor = defaultColor;
      Thread.Sleep(1);  // 1 ms
   }
}

Main startet den Thread

/*
   Dem Thread wird direkt der Name der Methode übergeben
*/
private static void Main()
{
   Thread.CurrentThread.Name = "Main";

   Console.WriteLine("begin Main");

   int[] intArr = { 1, 2, 3, 4, 5, 6, 7 } ;

   Thread th = new Thread(Call);
   th.Start(intArr); //Start receives the parameter

   for (int i = 1; i < 8; i++)
   {
      Console.WriteLine(Thread.CurrentThread.Name + "   " + i);
      Thread.Sleep(1);  // 1 ms
   }


   Console.WriteLine("end Main");
}

Hier srei Abläufe des Beispiels. Typisch für Threads ist der nicht genau vorhersagbare Ablauf.

csharp-threads-03.jpg

csharp-threads-04.jpg

csharp-threads-05.jpg

Der Thread mit einem Lambdaausdruck an Stelle einer Methode

private static void beispiel1d()
{
   Console.WriteLine("begin main");
   Thread th = new Thread( ob => Console.WriteLine(ob) ) ;
   th.Start("thread startet");
   Console.WriteLine("end main");
}


Threadname und ThreadId

Ein Thread in C# hat zunächst keinen Namen, man kann aber über Thread.CurrentThread.Name = "myThreadName" einen Namen vergeben. Dieseer Name ist dann nicht mehr zu ändern. Der Versuch ihn zu ändern führt zu einem Laufzeitfehler System.InvalidOperationException. Im Gegensatz dazu erhält der Thread von der CLR eine Id, die man über System.Threading.Thread.CurrentThread.ManagedThreadId abfragen kann. Diese Nummer ist jedoch keine Id des Betriebssystemsthreads. Man nennt daher die im Code selbst erzeuigten Threads gerne "ManagedThreads". Siehe auch die Diskussion auf stackoverflow hier (2009) und hier (2011).

Die Id des ManagedThread erhält man wie folgt

public static void Main(string[] args)
{
   Console.WriteLine("Main ManagedThreadId is " + System.Threading.Thread.CurrentThread.ManagedThreadId);

   Thread th = new Thread( () =>
   Console.WriteLine("My ManagedThreadId is " + System.Threading.Thread.CurrentThread.ManagedThreadId) );
   th.Start();
}


ThreadState

Die Enumeration ThreadState dient nur zur Information über die verschiedenen Zustände eines Threads. Die API sagt:
"Your code should never use the thread state to synchronize the activities of threads. ThreadState defines a set of all possible execution states for threads. Once a thread is created, it is in at least one of the states until it terminates. Threads created within the common language runtime are initially in the Unstarted state, while external threads that come into the runtime are already in the Running state. An Unstarted thread is transitioned into the Running state by calling Start. Not all combinations of ThreadState values are valid; for example, a thread cannot be in both the Aborted and Unstarted states."
Siehe http://msdn.microsoft.com/en-us/library/system.threading.threadstate.aspx .

EnumerationThreadState
Name Beschreibung
Aborted The thread state includes AbortRequested and the thread is now dead, but its state has not yet changed to Stopped.
AbortRequested The Thread.Abort method has been invoked on the thread, but the thread has not yet received the pending System.Threading.ThreadAbortException that will attempt to terminate it.
Background The thread is being executed as a background thread, as opposed to a foreground thread. This state is controlled by setting the Thread.IsBackground property.
RunningThe thread has been started, it is not blocked, and there is no pending ThreadAbortException.
Stopped The thread has stopped.
StopRequested The thread is being requested to stop. This is for internal use only.
Suspended The thread has been suspended.
SuspendRequested The thread is being requested to suspend.
Unstarted The Thread.Start method has not been invoked on the thread.
WaitSleepJoin The thread is blocked. This could be the result of calling Thread.Sleep or Thread.Join, of requesting a lock for example, by calling Monitor.Enter or Monitor.Wait or of waiting on a thread synchronization object such as ManualResetEvent.

Valid XHTML 1.0 Strict top Back Next Up Home