So I have this program that attempts to establish communication between two different threads, thread1 and thread2.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace Project1
{
class Class1
{
public static void thread1()
{
Console.WriteLine("1");
Console.WriteLine("t2 has printed 1, so we now print 2");
Console.WriteLine("t2 has printed 2, so we now print 3");
}
public static void thread2()
{
Console.WriteLine("t1 has printed 1, so we now print 1");
Console.WriteLine("t1 has printed 2, so we now print 2");
Console.WriteLine("t1 has printed 3, so we now print 3");
}
public static void Main() {
Thread t1 = new Thread(new ThreadStart(() => thread1()));
Thread t2 = new Thread(new ThreadStart(() => thread2()));
t1.Start();
t2.Start();
t2.Join();
t1.Join();
}
}
}
However, I want it to occur such that this line:
Console.WriteLine("1");
...gets executed first, while thread2 merely waits for this line to be executed. Then and only then would it print:
Console.WriteLine("t1 has printed 1, so we now print 1");
After this line is printed, then and only then would this line:
Console.WriteLine("t2 has printed 1, so we now print 2");
...get printed, and so on. So I want to change the code, so that the threads to communicate with each other so that the lines get printed in this order:
Console.WriteLine("1"); // from t1
Console.WriteLine("t1 has printed 1, so we now print 1"); // from t2
Console.WriteLine("t2 has printed 1, so we now print 2"); // from t1
Console.WriteLine("t1 has printed 2, so we now print 2"); // from t2
Console.WriteLine("t2 has printed 2, so we now print 3"); // from t1
Console.WriteLine("t1 has printed 3, so we now print 3"); // from t2
I understand what lock does, but it only applies if the two different threads are running on the same function. However, here, the two functions are different and thus I can't use lock here.
Any ideas?
It looks like you need Monitor.Wait and Monitor.Pulse. There's a free eBook out there on Threading (there are probably many but this one helped me).
You can use a static object to lock on, and then have your threads call Monitor.Pulse
to signal that they are "done with their turn" and Monitor.Wait
to "wait for their next turn". Here's an example implementation using your basic code:
public class Class1
{
// Use this to syncrhonize threads
private static object SyncRoot = new object();
// First "turn" goes to thread 1
private static int threadInControl = 1;
public static void thread1()
{
lock(SyncRoot) // Request exclusive access to SyncRoot
{
Console.WriteLine("1");
GiveTurnTo(2); // Let thread 2 have a turn
WaitTurn(1); // Wait for turn to be given to thread 1
Console.WriteLine("t2 has printed 1, so we now print 2");
GiveTurnTo(2); // Let thread 2 have a turn
WaitTurn(1); // Wait for turn to be given to thread 1
Console.WriteLine("t2 has printed 2, so we now print 3");
GiveTurnTo(2); // Let thread 2 have a turn
}
}
public static void thread2()
{
lock(SyncRoot) // Request exclusive access to SyncRoot
{
WaitTurn(2); // Wait for turn to be given to thread 2
Console.WriteLine("t1 has printed 1, so we now print 1");
GiveTurnTo(1); // Let thread 1 have a turn
WaitTurn(2); // Wait for turn to be given to thread 2
Console.WriteLine("t1 has printed 2, so we now print 2");
GiveTurnTo(1); // Let thread 1 have a turn
WaitTurn(2); // Wait for turn to be given to thread 2
Console.WriteLine("t1 has printed 3, so we now print 3");
GiveTurnTo(1); // Let thread 1 have a turn
}
}
// Wait for turn to use SyncRoot object
public static void WaitTurn(int threadNum)
{
// While( not this threads turn )
while (threadInControl != threadNum)
{
// "Let go" of lock on SyncRoot and wait utill
// someone finishes their turn with it
Monitor.Wait(SyncRoot);
}
}
// Pass turn over to other thread
public static void GiveTurnTo(int nextThreadNum)
{
threadInControl = nextThreadNum;
// Notify waiting threads that it's someone else's turn
Monitor.Pulse(SyncRoot);
}
public static void void Main()
{
Thread t1 = new Thread(new ThreadStart(() => Class1.thread1()));
Thread t2 = new Thread(new ThreadStart(() => Class1.thread2()));
t1.Start();
t2.Start();
t2.Join();
t1.Join();
}
}
As for using the lock keyword, it is not limited to synchronization within the same function. Lock "guarantees" exclusive access to a resource (object) to a single thread (by exclusive, I mean that only one thread can obtain a lock on that resource at a time, locking does not prevent other threads from simply accessing the object itself).
To simplify it, lock(someObject)
is like a thread getting in line to use someOject
and then waiting until all other threads in front of it have finished their turn before continuing. A thread ends its "turn" when it leaves the scope of the lock statement (unless you add things like Monitor.Pulse
or Monitor.Wait
).
Use a ManualResetEvent. Wait for it in thread 2 and set it in thread1 after Console.WriteLn() http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With