Reputation: 365
I'm wondering what is the best way to implement communication between two threads. I have one thread that generates random number(class Sender) and now I want to have another thread(class Receiver) that 'll receive generated random number. This is Sender:
public class Sender
{
public int GenerateNumber(){
//some code
return randomNumber;
}
}
Afcourse in Main function I'll start those threads:
static void Main(string[] args){
Sender _sender=new Sender();
Thread thread1=new Thread(new ThreadStart(_sender.GenerateNumber));
}
I appreciate your help
Upvotes: 14
Views: 46643
Reputation: 10280
Here's a possible approach using a WaitHandle:
class Program
{
static void Main(string[] args)
{
Sender _sender = new Sender();
Receiver _receiver = new Receiver();
using (ManualResetEvent waitHandle = new ManualResetEvent(false))
{
// have to initialize this variable, otherwise the compiler complains when it is used later
int randomNumber = 0;
Thread thread1 = new Thread(new ThreadStart(() =>
{
randomNumber = _sender.GenerateNumber();
try
{
// now that we have the random number, signal the wait handle
waitHandle.Set();
}
catch (ObjectDisposedException)
{
// this exception will be thrown if the timeout elapses on the call to waitHandle.WaitOne
}
}));
// begin receiving the random number
thread1.Start();
// wait for the random number
if (waitHandle.WaitOne(/*optionally pass in a timeout value*/))
{
_receiver.TakeRandomNumber(randomNumber);
}
else
{
// signal was never received
// Note, this code will only execute if a timeout value is specified
System.Console.WriteLine("Timeout");
}
}
}
}
public class Sender
{
public int GenerateNumber()
{
Thread.Sleep(2000);
// http://xkcd.com/221/
int randomNumber = 4; // chosen by fair dice role
return randomNumber;
}
}
public class Receiver
{
public void TakeRandomNumber(int randomNumber)
{
// do something
System.Console.WriteLine("Received random number: {0}", randomNumber);
}
}
Task<TResult>
class in .NET 4 pointed out by Jon Skeet in his answer. Credit goes to him for pointing it out. Thanks a lot, Jon. I haven't had a reason to use that class yet, and was pleasantly surprised when I saw how easy it was to use.
Aside from performance benefits that you gain under the hood from using this class, writing equivalent code using the Task<TResult>
class seems to be much easier. For instance, body of the Main method above could be rewritten as shown below:
Sender _sender = new Sender();
Receiver _receiver = new Receiver();
Task<int> getRandomNumber = Task.Factory.StartNew<int>(_sender.GenerateNumber);
// begin receiving the random number
getRandomNumber.Start();
// ... perform other tasks
// wait for up to 5 seconds for the getRandomNumber task to complete
if (getRandomNumber.Wait(5000))
{
_receiver.TakeRandomNumber(getRandomNumber.Result);
}
else
{
// the getRandomNumber task did not complete within the specified timeout
System.Console.WriteLine("Timeout");
}
If you have no need to specify a timeout for the task and are content to wait indefinitely for it to finish, then you can write this using even less code:
Sender _sender = new Sender();
Receiver _receiver = new Receiver();
Task<int> getRandomNumber = Task.Factory.StartNew<int>(_sender.GenerateNumber);
// begin receiving the random number
getRandomNumber.Start();
// ... perform other tasks
// accessing the Result property implicitly waits for the task to complete
_receiver.TakeRandomNumber(getRandomNumber.Result);
Upvotes: 10
Reputation: 570
The "best" way to implement communication between two threads really depends on what needs to be communicated. Your example seems to be a classic producer/consumer problem. I would use a Synchronized Queue. Check out the MSDN documentation for Synchronized Collections. You can use the Queue.Synchronized method to get a synchronized wrapper for a Queue object. Then, have the producer thread call Enqueue() and the consumer call Dequeue().
Upvotes: 2
Reputation: 15293
If all you are doing is generating a random number in the one thread I would probably create a thread safe object that does this instead.
lock(syncRoot)
{
myCurrentRandom = Generate();
return myCurrentRandom;
}
Upvotes: 0
Reputation: 1499740
If you're using .NET 4, I would suggest using a higher level abstraction: Task<TResult>
. Your first thread can schedule the task (which may end up creating a thread, or be scheduled on an existing task-handling thread) and can then check for status, block for the result etc as it sees fit.
If you want to do more than a one-shot task, you may want to use a producer/consumer queue - again, .NET 4 helps with that via BlockingCollection<T>
.
Upvotes: 23
Reputation: 25337
You will need some kind of resource (list, queue, etc.) shared between sender and receiver. And you will have to synchronize the access to this resource, otherwise you will not be able to pass data between the threads.
Upvotes: 4