2009-04-09 2 views
12

Как можно использовать модели Производитель/потребитель в C# с использованием События и делегаты? Что мне нужно, чтобы следить за тем, когда речь заходит о ресурсах при использовании этих шаблонов дизайна? Есть ли какие-либо краевые случаи, о которых я должен знать?Внедрение шаблона производителя/потребителя в C#

+5

Это действительный вопрос ИМО. И я не думаю, что не так вежливо игнорировать вопрос как домашнюю работу. – Sung

+5

@ Сун Майстер: Я не могу согласиться с вами больше! –

+5

Домашнее задание или нет, это общая проблема, и она заслуживает ответа. – mpen

ответ

13

Посмотрите на эту link на MSDN

+4

Ссылка теперь отмечена как «устаревшая». – ctford

+2

это может помочь. Http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx#vcwlkthreadingtutorialexample2synchronizing – Amitd

+0

Это [новая ссылка] (https://msdn.microsoft .com/en-us/library/yy12yx1f (v = vs.90) .aspx) – VivekDev

7

Поскольку выше ссылка теперь Устаревшие ..

пожалуйста попробуйте следующее

Producer/очереди потребителей

http://www.albahari.com/threading/part2.aspx

Пример 2: Синхронизация двух потоков: производитель и потребитель

http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx#vcwlkthreadingtutorialexample2synchronizing

+0

Статья на albahari.com очень обширна. Благодаря! –

1

Я знаю, что эта нить совсем немного старая, но так как я наткнулся на него иногда в моих поисках, я решил поделиться этим производитель-потребитель кодом для людей, задаваясь вопросом, как реализовать простой общий производитель-потребитель очереди заданий.

Класс работы используется для хранения вызова метода объекта в форме делегата. Затем делегат вызывается, когда задание обрабатывается. Любые соответствующие аргументы также сохраняются в этом классе Job.

С помощью этого простого шаблона можно добиться многопоточности в процессах enqueue AND dequeue. На самом деле это самая простая часть: многопоточность вызывает новые проблемы для вашего кода, вы заметите их позже ;-)

Я изначально разместил этот код в этом thread.

using System; 
using System.Collections.Concurrent; 
using System.Diagnostics; 
using System.Threading; 

// Compiled and tested in: Visual Studio 2017, DotNET 4.6.1 

namespace MyNamespace 
{ 
    public class Program 
    { 
     public static void Main(string[] args) 
     { 
      MyApplication app = new MyApplication(); 
      app.Run(); 
     } 
    } 

    public class MyApplication 
    { 
     private BlockingCollection<Job> JobQueue = new BlockingCollection<Job>(); 
     private CancellationTokenSource JobCancellationTokenSource = new CancellationTokenSource(); 
     private CancellationToken JobCancellationToken; 
     private Timer Timer; 
     private Thread UserInputThread; 



     public void Run() 
     { 
      // Give a name to the main thread: 
      Thread.CurrentThread.Name = "Main"; 

      // Fires a Timer thread: 
      Timer = new Timer(new TimerCallback(TimerCallback), null, 1000, 2000); 

      // Fires a thread to read user inputs: 
      UserInputThread = new Thread(new ThreadStart(ReadUserInputs)) 
      { 
       Name = "UserInputs", 
       IsBackground = true 
      }; 
      UserInputThread.Start(); 

      // Prepares a token to cancel the job queue: 
      JobCancellationToken = JobCancellationTokenSource.Token; 

      // Start processing jobs: 
      ProcessJobs(); 

      // Clean up: 
      JobQueue.Dispose(); 
      Timer.Dispose(); 
      UserInputThread.Abort(); 

      Console.WriteLine("Done."); 
     } 



     private void ProcessJobs() 
     { 
      try 
      { 
       // Checks if the blocking collection is still up for dequeueing: 
       while (!JobQueue.IsCompleted) 
       { 
        // The following line blocks the thread until a job is available or throws an exception in case the token is cancelled: 
        JobQueue.Take(JobCancellationToken).Run(); 
       } 
      } 
      catch { } 
     } 



     private void ReadUserInputs() 
     { 
      // User input thread is running here. 
      ConsoleKey key = ConsoleKey.Enter; 

      // Reads user inputs and queue them for processing until the escape key is pressed: 
      while ((key = Console.ReadKey(true).Key) != ConsoleKey.Escape) 
      { 
       Job userInputJob = new Job("UserInput", this, new Action<ConsoleKey>(ProcessUserInputs), key); 
       JobQueue.Add(userInputJob); 
      } 
      // Stops processing the JobQueue: 
      JobCancellationTokenSource.Cancel(); 
     } 

     private void ProcessUserInputs(ConsoleKey key) 
     { 
      // Main thread is running here. 
      Console.WriteLine($"You just typed '{key}'. (Thread: {Thread.CurrentThread.Name})"); 
     } 



     private void TimerCallback(object param) 
     { 
      // Timer thread is running here. 
      Job job = new Job("TimerJob", this, new Action<string>(ProcessTimer), "A job from timer callback was processed."); 
      JobQueue.TryAdd(job); // Just enqueues the job for later processing 
     } 

     private void ProcessTimer(string message) 
     { 
      // Main thread is running here. 
      Console.WriteLine($"{message} (Thread: {Thread.CurrentThread.Name})"); 
     } 
    } 



    /// <summary> 
    /// The Job class wraps an object's method call, with or without arguments. This method is called later, during the Job execution. 
    /// </summary> 
    public class Job 
    { 
     public string Name { get; } 
     private object TargetObject; 
     private Delegate TargetMethod; 
     private object[] Arguments; 

     public Job(string name, object obj, Delegate method, params object[] args) 
     { 
      Name = name; 
      TargetObject = obj; 
      TargetMethod = method; 
      Arguments = args; 
     } 

     public void Run() 
     { 
      try 
      { 
       TargetMethod.Method.Invoke(TargetObject, Arguments); 
      } 
      catch(Exception ex) 
      { 
       Debug.WriteLine($"Unexpected error running job '{Name}': {ex}"); 
      } 
     } 

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