Suppose we have following code and we are required to print “foo” and “bar” alternately. Each of method foo and bar will be running on different thread.
class Program { static void Main(string[] args) { FooBar fooBar = new FooBar(5); var taskFoo = Task.Run(() => fooBar.Foo(() => Console.WriteLine("foo"))); var taskBar = Task.Run(() => fooBar.Bar(() => Console.WriteLine("bar"))); Task.WaitAll(taskFoo, taskBar); Console.WriteLine("All threads are complete"); } } public class FooBar { private int n; public FooBar(int n) { this.n = n; } public void Foo(Action printFoo) { for (int i = 0; i < n; i++) { printFoo(); } } public void Bar(Action printBar) { for (int i = 0; i < n; i++) { printBar(); } } }
There are some synchronization techniques that can be used, such as: Semaphore, ManualResetEvent, or AutoResetEvent.
In this case, we will use AutoResetEvent. Then above code will be as below:
public class FooBar { AutoResetEvent _blockThread1 = new AutoResetEvent(false); AutoResetEvent _blockThread2 = new AutoResetEvent(true); private int n; public FooBar(int n) { this.n = n; } public void Foo(Action printFoo) { for (int i = 0; i < n; i++) { _blockThread2.WaitOne(); printFoo(); _blockThread1.Set(); } } public void Bar(Action printBar) { for (int i = 0; i < n; i++) { _blockThread1.WaitOne(); printBar(); _blockThread2.Set(); } } }
Because we initialize AutoResetEvent constructor to be “true”, then the thread that is waiting will be signaled to run the thread. Thus it will allow “Foo” to be printed first. After printing “Foo”, it will execute _blockThread1.Set() that will signal _blockThread1 to run a thread to print “Bar”. After printing “Bar”, it will signal _blockThread2 that is waiting to run. It will happen so on until all the threads finish printing “Foo” and “Bar” alternately.
If you are interested with the code, you can find on my github.