Threading, связь между двумя потоками С#
Мне интересно, как лучше всего реализовать связь между двумя потоками. У меня есть один поток, который генерирует случайное число (класс Sender), и теперь я хочу иметь другой поток (класс Receiver), который получит генерируемое случайное число.
Это отправитель:
public class Sender
{
public int GenerateNumber(){
//some code
return randomNumber;
}
}
Работа в главной функции Я начну эти темы:
static void Main(string[] args){
Sender _sender=new Sender();
Thread thread1=new Thread(new ThreadStart(_sender.GenerateNumber));
}
Я ценю вашу помощь
Ответы
Ответ 1
Если вы используете .NET 4, я бы предложил использовать абстракцию более высокого уровня: Task<TResult>
. Ваша первая нить может планировать задачу (которая может в конечном итоге создать поток или быть запланирована в существующем потоке обработки задачи), а затем проверить состояние, заблокировать результат и т.д., Как он считает нужным.
Если вы хотите выполнить более однозадачную задачу, вы можете захотеть использовать очередь производителей/потребителей - опять же,.NET 4 помогает с помощью BlockingCollection<T>
.
Ответ 2
Здесь возможен подход с использованием 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>
в .NET 4, который указал Джон Скит в его ответе. Кредит идет к нему, чтобы указать на это. Большое спасибо, Джон. У меня еще не было причины использовать этот класс, и я был приятно удивлен, когда увидел, как легко его использовать.
Помимо преимуществ производительности, которые вы получаете под капотом от использования этого класса, писать эквивалентный код с использованием класса Task<TResult>
представляется намного проще. Например, тело основного метода выше можно переписать, как показано ниже:
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");
}
Если вам не нужно указывать тайм-аут для задачи и контент ждать бесконечно, чтобы завершить ее, вы можете записать это, используя еще меньше кода:
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);
Ответ 3
Вам понадобится какой-то ресурс (список, очередь и т.д.), совместно используемый между отправителем и получателем. И вам придется синхронизировать доступ к этому ресурсу, иначе вы не сможете передавать данные между потоками.
Ответ 4
Если все, что вы делаете, генерирует случайное число в одном потоке, я бы, вероятно, создал объект, защищенный потоком, который делает это вместо этого.
lock(syncRoot)
{
myCurrentRandom = Generate();
return myCurrentRandom;
}
Ответ 5
"Лучший" способ реализовать связь между двумя потоками действительно зависит от того, что нужно сообщить. Ваш пример кажется классической проблемой производителя/потребителя. Я бы использовал синхронизированную очередь. Ознакомьтесь с документацией MSDN для Synchronized Collections. Вы можете использовать метод Queue.Synchronized, чтобы получить синхронизированную оболочку для объекта Queue. Затем попросите поток производителя вызвать Enqueue() и потребительский вызов Dequeue().