Существует ли стандартная схема, которой нужно следовать при ожидании N числа асинхронных методов? - PullRequest
11 голосов
/ 29 июня 2011
public class FooDataRepository
{
    private MyServiceReferenceClient Client { get; set; }

    public void FooClass()
    {
        Client = new MyServiceReferenceClient();
        Client.SaveFooCompleted += Client_SaveFooCompleted;
        Client.SaveBarCompleted += Client_SaveBarCompleted;
    }

    private void Client_SaveFooCompleted(Object sender, EventArgs e) { ... }

    private void Client_SaveBarCompleted(Object sender, EventArgs e) { ... }

    public void SaveFoo(Foo foo)
    {
        Client.SaveFooAsync(foo);

        foreach (var bar in foo.Bars)
            Client.SaveBarAsync(bar);
    }

}

Я хочу сделать что-то в классе FooDataRepository после завершения SaveFooAsync и всех методов SaveBarAsync. Существует ли стандартная схема для попытки сделать что-то одно, основанное на количестве завершенных асинхронных вызовов N?

Ответы [ 3 ]

5 голосов
/ 29 июня 2011

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

Task[] tasks = new Task[3]
{
    Task.Factory.StartNew(() => MethodA()),
    Task.Factory.StartNew(() => MethodB()),
    Task.Factory.StartNew(() => MethodC())
};

//This will not block.
Task.Factory.ContinueWhenAll(tasks, completedTasks => { RunSomeMethod(); });

РЕДАКТИРОВАТЬ:

Что касается вашего вопроса о составлении вызовов асинхронных методов с задачами, если в классе есть методы Begin / End для вызова метода асинхронным способом, вы можете использовать Task.FromAsync

В качестве альтернативы вы также можете использовать Rxдля вызова нескольких методов асинхронно и последующего наблюдения, когда все они завершены.Посмотрите на этот вопрос: Можно ли создать ярлык асинхронного шаблона Begin / End, используя событие для создания моего AsyncResult?(C # .Net 3.5, WCF)

1 голос
/ 29 июня 2011

Если можете, используйте Задачи , а затем выполните Task.WaitAll.Например:

Task[] tasks = new Task[3]
{
    Task.Factory.StartNew(() => MethodA()),
    Task.Factory.StartNew(() => MethodB()),
    Task.Factory.StartNew(() => MethodC())
};

//Block until all tasks complete.
Task.WaitAll(tasks);

// Continue on this thread...

Если у вас нет доступа к синхронным методам, вы можете использовать метод, подобный описанному в Задачи и Асинхронный шаблон на основе событий , для преобразования вашего EAP библиотека в одну, которая использует задачи.С другой стороны, Rx предоставляет несколько способов решения этой проблемы.

По сути, лучший совет - по возможности использовать Задачи.Если вашему приложению требуется более детальный контроль, внимательно посмотрите на Rx - он обеспечивает фильтрацию LINQ для событий и асинхронные методы.

0 голосов
/ 22 сентября 2011

Я не знаю, насколько «стандартной» является моя идея, но я только недавно начал использовать некоторые асинхронные вызовы с WCF Data Services и Silverlight. В некоторых случаях у нас есть коллекции, которые очень похожи на «коллекции», представленные шаблоном Repository. Мы выдаем запрос для получения элементов из службы данных WCF, но затем каждый возвращаемый элемент должен быть «загружен» по очереди (т. Е. Каждый элемент имеет метод Load, который выполняется асинхронно и может в конечном итоге выдать свой собственный запрос службы данных WCF) , Во время всей этой загрузки любой из наших графических интерфейсов, который зависит от загружаемых данных (определенные вкладки), «блокируется» (отображается индикатор выполнения) до тех пор, пока он не будет загружен. В некоторых случаях нам нужно загрузить две коллекции, потому что одна имеет отношение к другой. Мы используем шаблон обратного вызова с сервисом данных WCF, поэтому после вызова каждого из обратных вызовов (при загрузке нескольких коллекций) мы знаем, что наша задача «загрузки» завершена.

Таким образом, применение нашего шаблона к вашему случаю приведет к чему-то вроде этого (грубый псевдокод с использованием вашего примера кода в качестве отправной точки)

public class FooDataRepository 
{
     bool fooCompleted = false;
     bool barCompleted = false;
     int barsSaved = 0;
     int barCount = 0;

     private MyServiceReferenceClient Client { get; set; }
     public void FooClass()
     {
         Client = new MyServiceReferenceClient();
         Client.SaveFooCompleted += Client_SaveFooCompleted;
         Client.SaveBarCompleted += Client_SaveBarCompleted;
     }

     private void Client_SaveFooCompleted(Object sender, EventArgs e) 
     {
       fooCompleted = true;
       if (barCompleted)
       {
         SaveCompleted();
       }
     }

     private void Client_SaveBarCompleted(Object sender, EventArgs e) 
     {
       Interlocked.Increment(barsSaved);
       barCompleted = barsSaved == barCount;
       if (fooCompleted)
       {
         SaveCompleted();
       }
     }

     private void SaveCompleted()
     {
       //Do whatever you want to do when foo and all bars have been saved
     }

     public void SaveFoo(Foo foo)
     {
        fooCompleted = barCompleted = false;
        barCount = foo.Bars.Count;
        barsSaved = 0;

        Client.SaveFooAsync(foo);
        foreach (var bar in foo.Bars)
            Client.SaveBarAsync(bar);
     }
 } 

Если честно, я не уверен, является ли это "хорошим" паттерном или нет. Эти вызовы являются асинхронными и имеют обратный вызов / событие, которое вызывается / вызывается, когда работа завершена. Этот шаблон работает достаточно хорошо, чтобы мы могли отключить индикатор выполнения после загрузки всех данных.

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

Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...