2013-03-03 2 views
0

Предположим, что у нас есть некоторые методы, которые выполняют большую работу.Очередь нескольких задач с обратным вызовом

Первоначально мы не знаем, сколько методов существует (может быть 1 и может быть 10).

В коде это выглядит следующим образом:

public interface IWorker 
{ 
    void DoWork(DataContainer data); 
} 

И несколько классов, которые реализуют этот интерфейс. Затем у нас есть список экземпляров.

List<IWorker> workers = new List<IWorker>(); 

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

public void Callback() 
{ 
    Console.WriteLine("everything done"); 
} 

Есть ли способ сделать это без написания пользовательских оберток или так? С ThreadPool, Tasks, Parallel?

Как я знаю, поток блоков Parrallel до тех пор, пока задачи не будут завершены, поэтому это не преферирование.

При создании задачи должен быть метод без параметров, как я видел, так что это тоже не хорошо.

В ThreadPool есть возможность использовать метод QueueUserWorkItem, но с помощью этого метода я не получу один обратный вызов полного завершения.

Конечно, я могу создать свою собственную оболочку, которая будет реализовывать желаемую функциональность с помощью ThreadPool, но цель состоит в том, чтобы сделать это без написания такого.

Помогите пожалуйста? Спасибо.

ответ

3

Вы ищете TPL и класс Task.

Создание Task для каждой операции, а затем вызвать Task.WhenAll получить агрегатной задачу

+0

Да, это звучит хорошо, но задача в его конструкторе ждет метода без входных параметров. И мои методы имеют такой. – steavy

+1

@steavy Нет необходимости вводить параметры ввода в задачу, потому что вам не нужно планировать задачу после ее создания, в других работах нет необходимости, чтобы планировщик задач имел что-либо, чтобы предоставить делегат. Вместо этого просто закройте любые переменные, которые могут потребоваться в качестве параметров для вашей функции, используя лямбда, а не передавая значения напрямую. – Servy

+0

спасибо. Поскольку вы были первым, кто предложил Task.WhenAll обозначил как правильный ответ :) – steavy

0

звучит как главный кандидат на CountdownEvent класс:

List<IWorker> workers = new List<IWorker>(); 
using (CountdownEvent e = new CountdownEvent(workers.Count)) 
{ 
    foreach (IWorker worker in workers) 
    { 
     // Dynamically increment signal count. 
     e.AddCount(); 
     // run work itself on another thread 
     ThreadPool.QueueUserWorkItem(delegate(object state) 
     { 
      try 
      { 
       ((IWorker)state[0]).DoWork((DataContainer)state[1]); 
      } 
      finally 
      { 
       e.Signal(); 
      } 
     }, 
     // pass required parameters for block of work 
     new object[] { worker, dataForWorker }); 
    } 

    // wait for all workers to finish 
    e.Wait(); 
    // run callback code 
} 
2

Вы ищете Task.WhenAll. Создайте кучу задач, которые делают то, что вы хотите, затем ждите все задачи и продолжайте с обратным вызовом. Я разделил асинхронную версию DoWork-метода - если вы всегда будете называть ее асинхронно, вам необязательно это делать.

public interface IWorker 
{ 
    Task DoWorkAsync(string data); 
    void DoWork(string data); 
} 

public class Worker : IWorker 
{ 
    public Task DoWorkAsync(string data) 
    { 
     return Task.Run(() => DoWork(data)); 
    } 

    public void DoWork(string data) 
    { 
     Console.WriteLine(data); 
     Thread.Sleep(100); 
    } 
} 

public class Runner 
{ 
    public void Callback() 
    { 
     Console.WriteLine("Everything done"); 
    } 

    public void Run() 
    { 
     var workers = new List<IWorker> {new Worker(), new Worker(), new Worker()}; 
     var tasks = workers.Select(t => t.DoWorkAsync("some data")); 

     Task.WhenAll(tasks).ContinueWith(task => Callback()); 

     Console.WriteLine("Waiting"); 
    } 
} 
Смежные вопросы