using System; using System.Collections; using System.Threading; // Producer-Consumerパターン // WaitとPulseAllのサンプル public class List3 { static void Main () { Table table = new Table(); // 共有スペースであるテーブル for(int i=0; i < 5; i++) { (new Producer(table)).ThreadStart(); (new Consumer(table)).ThreadStart(); } } } public class Producer // 料理人 { private readonly Table table; private static int id = 0; private readonly object lockObject = new object(); public Producer(Table table) { this.table = table; } public void ThreadStart() { (new Thread(new ThreadStart(Produce))).Start(); } private void Produce() { string dish; while (true) { lock (lockObject) // idの値を排他制御するためのロック { dish = "No." + id++; } // 料理(dish)を作成して、テーブルに置く Console.WriteLine(dish + " 作成"); table.put(dish); Thread.Sleep(5000); // 5秒ごとに1料理作成できる } } } public class Consumer // 客 { private readonly Table table; public Consumer(Table table) { this.table = table; } public void ThreadStart() { (new Thread(new ThreadStart(Consume))).Start(); } public void Consume() { while(true) { // テーブルから料理を取り、消費する Console.WriteLine(table.take() + " テーブルから取り消費"); Thread.Sleep(10000); // 消費には時間がかかる } } } public sealed class Table // テーブル { // テーブルに置くことができる料理の最大数 private readonly int max = 3; private readonly Queue queue = new Queue(); // テーブルの実体 private readonly object lockObject = new object(); public void put(string dish) { Monitor.Enter(lockObject); try { while (queue.Count >= max) { // テーブルがいっぱいで料理を置くことができなかったら、 // ウェイトセットに入る Monitor.Wait(lockObject); } queue.Enqueue(dish); Console.WriteLine(dish + " テーブルに置かれた"); Monitor.PulseAll(lockObject); // ウェイトセットのスレッドを起こす } catch { } finally { Monitor.Exit(lockObject); } } public string take() { Monitor.Enter(lockObject); string dish = string.Empty; try { while (queue.Count == 0) { // テーブルに料理がなかったら、ウェイトセットに入る Monitor.Wait(lockObject); } dish = queue.Dequeue() as string; Monitor.PulseAll(lockObject); // ウェイトセットのスレッドを起こす } catch { } finally { Monitor.Exit(lockObject); } return dish; } }