2015-09-01 5 views
0

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

После некоторого поиска я думаю, что могу использовать массив из AutoResetEvents, а затем позвонить WaitHandle.WaitAll, чтобы дождаться их. Что-то вроде:

WaitHandle[] waitHandles = new WaitHandle[] 
{ 
    new AutoResetEvent(false), 
    new AutoResetEvent(false) 
}; 

DoWorkA((callback) => 
{ 
    waitHandles[0].Set(); 
}); 

DoWorkB((callback) => 
{ 
    waitHandles[1].Set(); 
}); 

// wait for DoWorkA and DoWorkB to finish 
WaitHandles.WaitAll(waitHandles); 

// continue with the processing 

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

EDIT: После попытки использования этого кода я обнаружил, что он не работает. Нить UI останавливается на WaitHandles.WaitAll(waitHandles) и обратные вызовы никогда не закончатся ...

Внутри методов DoWork есть код время запуска asnychronously на фоне потока, а затем из этого фонового потока я пытаюсь запустить функцию обратного вызова через BeginInvoke называемый на управление из потока пользовательского интерфейса. Однако обратный вызов никогда не запускается в потоке пользовательского интерфейса. Любые идеи, что я делаю неправильно?

EDIT 2: я понял, почему он не работает - поток вызова WaitHandles.WaitAll(waitHandles) тот же поток, что обратные вызовы пытаются работать на (нить UI). Поэтому, конечно, если поток ожидает сигнала, обратные вызовы никогда не будут выполняться в этом же потоке, и поэтому Set() никогда не будет вызван. WaitAll (waitHandles) и Set() должны работать, если они не предназначены для запуска в одном потоке. Я думаю, мне нужно исправить это, чтобы обратные вызовы выполнялись в фоновом потоке, пока поток пользовательского интерфейса ожидает. Пожалуйста, дайте мне знать, если мое мнение здесь неверно.

ответ

2

Да, использование событий - разумный способ подождать, когда будет выполнено многократное завершение обратного вызова, а для 2.0 это, вероятно, лучшее, что вы можете сделать, если не можете использовать внешние библиотеки.

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

Если вы можете использовать 4.5+, чем методы перезаписи с async/await, это может быть лучшим вариантом.

// use "async Task(...)" as equivalent of "void DoWorkA(...)". 
    async Task<int> DoWorkAAsync(int arg1) 
    { 
    var serviceResult = await service.CallAsync(arg1); 
    return ParseServiceResult(serviceResult); 
    } 

    async Task<int> DoWorkBAsync() 
    { 
    ... await SomeAsync(1,2,3);... 
    return someResult; 
    } 

    var tasks = new[] {DoWorkAAsync(42), DoWorkBAsync() }; 
    await Task.WhenAll(tasks); 

    var resultOfA = tasks[0].Result; 
+0

Благодарим за информацию об 4,5 (только для учебных целей). Тем не менее, я не могу переписать методы, которые я вызываю (DoWorkA, DoWorkB в примере), я должен их вызвать, так как мне нужно работать с их обратными вызовами ... Что касается использования семафора, та работа? – JPProgrammer

+1

@JPProgrammer Семафор - начните с номера обратного вызова и подождите, ReleaseSemaphore в каждом обратном вызове, в результате исходный поток будет продолжаться, когда релиз называется number_of_callbacks раз. Async/wait: ознакомьтесь с http://stackoverflow.com/questions/15575579/rewriting-code-to-take-advantage-of-c-sharp-async-feature для повторного использования методов с обратными вызовами в мире async/wait. –