Here are four examples of synchronization, firing three pieces of work and blocking until any of the three complete:
- Tasks
- ManualResetEvents with a WaitHandle
- ManualResetEvent
- Monitor Wait and Pulse
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Synchronisation
{
internal class Program
{
private static void Main(string[] args)
{
new TaskWaitAnyExample().Execute();
new ManualResetEventWithWaitHandle().Execute();
new ManualResetEventExample().Execute();
new MonitorWaitAndPulse().Execute();
}
}
public abstract class Example
{
protected string name = "Example";
public abstract void Execute();
protected void MethodA()
{
Console.WriteLine("{0}: Entering MethodA", name);
Thread.Sleep(20000);
Console.WriteLine("{0}: Exiting MethodA", name);
}
protected void MethodB()
{
Console.WriteLine("{0}: Entering MethodB", name);
Thread.Sleep(10000);
Console.WriteLine("{0}: Exiting MethodB", name);
}
protected void MethodC()
{
Console.WriteLine("{0}: Entering MethodC", name);
Thread.Sleep(1000);
Console.WriteLine("{0}: Exiting MethodC", name);
}
}
public class TaskWaitAnyExample : Example
{
public override void Execute()
{
name = "TaskWaitAnyExample";
Console.WriteLine("{0} (from .NET 4.0):", name);
var taskA = Task.Factory.StartNew(MethodA);
var taskB = Task.Factory.StartNew(MethodB);
var taskC = Task.Factory.StartNew(MethodC);
Console.WriteLine("Waiting for one task to finish");
Task.WaitAny(taskA, taskB, taskC);
Console.WriteLine("Signal received, time to move on{0}", Environment.NewLine);
}
}
public class ManualResetEventWithWaitHandle : Example
{
public override void Execute()
{
name = "ManualResetEventWithWaitHandle";
Console.WriteLine("{0} (from .NET 1.1):", name);
WaitHandle[] waitHandles = new WaitHandle[]
{
new ManualResetEvent(false),
new ManualResetEvent(false),
new ManualResetEvent(false)
};
ThreadPool.QueueUserWorkItem(o => { MethodA(); ((ManualResetEvent)waitHandles[0]).Set(); });
ThreadPool.QueueUserWorkItem(o => { MethodB(); ((ManualResetEvent)waitHandles[1]).Set(); });
ThreadPool.QueueUserWorkItem(o => { MethodC(); ((ManualResetEvent)waitHandles[2]).Set(); });
Console.WriteLine("Waiting for one signal that one work item has finished");
WaitHandle.WaitAny(waitHandles);
Console.WriteLine("Signal received, time to move on{0}", Environment.NewLine);
}
}
public class ManualResetEventExample : Example
{
private readonly ManualResetEvent manualResetEvent = new ManualResetEvent(false);
public override void Execute()
{
name = "ManualResetEvent";
Console.WriteLine("{0} (from .NET 1.1):", name);
ThreadPool.QueueUserWorkItem(o => { MethodA(); manualResetEvent.Set(); });
ThreadPool.QueueUserWorkItem(o => { MethodB(); manualResetEvent.Set(); });
ThreadPool.QueueUserWorkItem(o => { MethodC(); manualResetEvent.Set(); });
Console.WriteLine("Waiting for one signal that one work item has finished");
manualResetEvent.WaitOne();
Console.WriteLine("Signal received, time to move on{0}", Environment.NewLine);
}
}
public class MonitorWaitAndPulse : Example
{
readonly object locker = new object();
bool signalSent;
public override void Execute()
{
name = "Monitor Wait and Pulse";
Console.WriteLine("{0} (from .NET 1.1):", name);
ThreadPool.QueueUserWorkItem(o => DoWork(MethodA));
ThreadPool.QueueUserWorkItem(o => DoWork(MethodB));
ThreadPool.QueueUserWorkItem(o => DoWork(MethodC));
Console.WriteLine("Waiting for one signal that one work item has finished");
lock (locker)
while (!signalSent)
Monitor.Wait(locker);
Console.WriteLine("Signal received, time to move on{0}", Environment.NewLine);
}
private void DoWork(Action action)
{
action.Invoke();
lock (locker)
{
signalSent = true;
Monitor.Pulse(locker);
}
}
}
}