2012-01-06 3 views
11

Мне интересно, как лучше всего реализовать связь между двумя потоками. У меня есть один поток, который генерирует случайное число (класс Sender), и теперь я хочу иметь другой поток (класс Receiver), который получит генерируемое случайное число. Это Sender:Threading, связь между двумя потоками C#

public class Sender 
{ 
    public int GenerateNumber(){ 


     //some code 
     return randomNumber; 
    } 
} 

Afcourse в главной функции я начну эти темы:

static void Main(string[] args){ 

    Sender _sender=new Sender(); 
    Thread thread1=new Thread(new ThreadStart(_sender.GenerateNumber)); 

} 

Я ценю вашу помощь

+0

Создаются ли оба потока одновременно? Что делает приемник, пока он ждет Отправителя? Что делает Отправитель после генерации случайного числа? – cadrell0

+0

Является ли это .NET 4? – Yahia

+0

На главной функции я начну оба потока, и отправитель потока будет работать, а затем спать в течение 3 секунд. Также я начну экземпляр приемника потока, который будет получать данные ...basiclly threads ll work untui user press esc key ... reciever ll записывает на полученные номера консоли. – Avicena00

ответ

2

Вам нужен какой-то ресурс (список, очередь, и т. д.), разделяемых между отправителем и получателем. И вам придется синхронизировать доступ к этому ресурсу, иначе вы не сможете передавать данные между потоками.

19

Если вы используете .NET 4, я бы предложил использовать абстракцию более высокого уровня: Task<TResult>. Ваш первый поток может планировать задачу (которая может в конечном итоге создать поток или быть запланирована в существующем потоке обработки задачи), а затем проверить состояние, блокировать результат и т. Д., Как он считает нужным.

Если вы хотите выполнить более однозадачную задачу, вы можете захотеть использовать очередь производителя/потребителя - опять же, .NET 4 помогает в этом с помощью BlockingCollection<T>.

+1

I знал, что мне нужно читать C# по глубине, так что я начну этим вечером :) – Avicena00

+1

@AintMeBabe: Честно говоря, C# в глубине едва затрагивает новую задачу 'Task ' ... исследование параллельной библиотеки задач через MSDN сообщения в блогах могут иметь более непосредственную пользу, чем книга. Не то, чтобы я, конечно, отговаривал вас от долгого времени :) –

+0

Что делать, если у нас нет контроля над производителем; в моем контексте поток A (под моим контролем) что-то делает, и ему нужно дождаться, когда поток B поднимет событие; нить B порождается библиотекой. Я слушаю это событие. Как должен прослушиватель событий (работает в потоке B) уведомлять поток A? Я не могу придумать ничего, кроме ManualResetEvent. Есть ли лучший подход в .net 4? – Jimmy

0

Если все, что вы делаете, генерирует случайное число в одном потоке, я бы, вероятно, создал объект, защищенный потоком, который делает это вместо этого.

lock(syncRoot) 
{ 
    myCurrentRandom = Generate(); 
    return myCurrentRandom; 
} 
+0

Хорошая точка, это всего лишь одна функция, которую я тестирую, чтобы заставить все работать, после того, как отправитель потока будет иметь еще несколько методов – Avicena00

6

Вот возможный подход с использованием 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); 
+0

спасибо вам спасибо :) – Avicena00

+0

thumbs up !!!!!! – Avicena00

+0

@AintMeBabe - если вы используете .NET 4, то я должен рекомендовать вам проверить ответ Джона Скита. Похоже, вы можете сделать то же самое с гораздо меньшим количеством кода, используя класс 'Task '. –

0

«Лучший» способ реализовать связь между двумя потоками действительно зависит от того, что необходимо сообщить. Ваш пример кажется классической проблемой производителя/потребителя. Я бы использовал синхронизированную очередь. Ознакомьтесь с документацией MSDN для Synchronized Collections. Вы можете использовать метод Queue.Synchronized, чтобы получить синхронизированную оболочку для объекта Queue. Затем попросите поток производителя вызвать Enqueue() и потребительский вызов Dequeue().

Смежные вопросы