Home   Cover Cover Cover Cover
 

Thread-Synchronisation

A03.cs
using System;
using System.Threading;

class Buffer {
  char[] buf;
  int head, tail, n;
  int size;
  
  public Buffer(int size) {
    buf = new char[size];
    this.size = size;
    head = tail = n = 0;
  }

  public void Put(char ch) {
    Console.WriteLine(Thread.CurrentThread.Name + " calls Put");
    lock(this) {
      Console.WriteLine(Thread.CurrentThread.Name + " access granted");
      while (n == size) Monitor.Wait(this);
      buf[tail] = ch; tail = (tail + 1) % size; n++;
      Console.WriteLine(Thread.CurrentThread.Name + " ready: n=" + n);
      Console.WriteLine();
      Monitor.Pulse(this);
    }
  }

  public char Get() {
    Console.WriteLine(Thread.CurrentThread.Name + " calls Get");
    lock(this) {
      Console.WriteLine(Thread.CurrentThread.Name + " access granted");
      while (n == 0) Monitor.Wait(this);
      char ch = buf[head]; head = (head + 1) % size; n--;
      Console.WriteLine(Thread.CurrentThread.Name + " ready: n=" + n);
      Console.WriteLine();
      Monitor.Pulse(this);
      return ch;
    }
  }
}

class BufferTest {
  static Buffer buf = new Buffer(4);
  static Random rand = new Random();
  
  static void Produce() {
    for (int i = 0; i < 5; i++) {
      buf.Put('x');
      Thread.Sleep(rand.Next(1000));
    }
  }

  static void Consume() {
    for (int i = 0; i < 5; i++) {
      char ch = buf.Get();
      Thread.Sleep(rand.Next(100));
    }
  }

  static void Main(string[] arg) {
    Thread p1 = new Thread(new ThreadStart(Produce));
    Thread p2 = new Thread(new ThreadStart(Produce));
    Thread c1 = new Thread(new ThreadStart(Consume));
    Thread c2 = new Thread(new ThreadStart(Consume));
    p1.Name = "Prod1"; p2.Name = "Prod2";
    c1.Name = "Cons1"; c2.Name = "Cons2";
    p1.Start(); p2.Start(); c1.Start(); c2.Start();
  }
}

Dieses Programm liefert zum Beispiel folgende Ausgabe:

Prod1 calls Put
Prod1 access granted
Prod2 calls Put
Cons1 calls Get
Cons2 calls Get
Prod1 ready: n=1

Prod2 access granted
Prod2 ready: n=2

Cons1 access granted
Cons1 ready: n=1

Cons2 access granted
Cons2 ready: n=0

Cons1 calls Get
Cons1 access granted
Prod2 calls Put
Prod2 access granted
Prod2 ready: n=1

Cons2 calls Get
Cons2 access granted
Cons2 ready: n=0

Prod1 calls Put
Prod1 access granted
Prod1 ready: n=1

Cons1 ready: n=0

Cons1 calls Get
Cons1 access granted
Cons2 calls Get
Cons2 access granted
Prod1 calls Put
Prod1 access granted
Prod1 ready: n=1

Cons1 ready: n=0

Cons1 calls Get
Cons1 access granted
Prod1 calls Put
Prod1 access granted
Prod1 ready: n=1

Cons2 ready: n=0

Cons2 calls Get
Cons2 access granted
Prod2 calls Put
Prod2 access granted
Prod2 ready: n=1

Cons1 ready: n=0

Cons1 calls Get
Cons1 access granted
Prod1 calls Put
Prod1 access granted
Prod1 ready: n=1

Cons2 ready: n=0

Cons2 calls Get
Cons2 access granted
Prod2 calls Put
Prod2 access granted
Prod2 ready: n=1

Cons1 ready: n=0

Prod2 calls Put
Prod2 access granted
Prod2 ready: n=1

Cons2 ready: n=0

Da die Consumer-Threads hier schneller sind als die Producer-Threads, finden sie immer wieder einmal einen leeren Puffer vor und müssen warten, bis ein Producer wieder Daten im Puffer abgelegt hat.