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().