Асинхронные вызовы с делегатом

В настоящее время я использую делегат для выполнения асинхронных вызовов в цикле for, проблема, с которой я сталкиваюсь, - как я узнаю, когда эти асинхронные вызовы завершены?

например:

         public delegate string GetMergeSectionCaller(string something1, out int threadId);

         public Dataset GetDataset (param1, param2) {
               int threadId;
               Dataset ds = new Dataset;

               using (myConnection) {
                   myConnection.StartConnection();
                   GetMergeSectionCaller caller = new GetMergeSectionCaller(GetMergeSection);
                   foreach (var r in anObjectList) {
                       IAsyncResult result = caller.BeginInvoke(r.ToString(), out threadId, null, null);
                   }

                   //I want to do here is wait for the above every single foreach BeginInvoke to finish; then do the below job
                   ds = GetFinalData();
               }
               //do more thing to ds here;
               return ds;
            }

         public void GetMergeSectionCaller(string something1, out int threadId) {
             //doing superlong long job
             //in my actual case, it's actually inserting data to db etc

             Thread.Sleep(5000);
             threadId = Thread.CurrentThread.ManagedThreadId;
         }

поэтому я попробовал разные подходы; например, передача обратного вызова моим BeginInvoke и EndInvoke, но все же - я не могу остановить работу остальной части кода, прежде чем я смогу завершить foreach;

может быть, я упускаю что-то довольно простое?.... Может кто-нибудь показать мне полный рабочий образец в зависимости от моего состояния?

1 ответ

Решение

У вас есть несколько вариантов. IAsyncResult имеет свойство WaitHandle, которое можно использовать для ожидания.

var results = new List<WaitHandle>();
foreach (var r in anObjectList) {
   results.Add(caller.BeginInvoke(r.ToString(), out threadId, null, null).WaitHandle);
}
// wait for all results to complete
WaitHandle.WaitAll(results.ToArray());

Другой вариант - создать ManualResetEvent и счетчик и сбросить событие из обратного вызова, когда счетчик достигнет 0. Преимущество этого метода заключается в том, что вы будете создавать только один ожидаемый объект, но вам также придется управлять счетчиком.,

И, наконец, еще один вариант - использовать новый API на основе задач, который обеспечивает намного лучшую абстракцию программирования для ожидания задач.

Некоторые другие вещи, на которые следует обратить внимание:

  • НЕ ИСПОЛЬЗУЙТЕ Thread.Sleep - можно использовать его для тестирования вашего кода, но как только вы убедились, что ваш асинхронный код работает, не используйте его!
  • НЕ полагайтесь на делегат BeginInvoke - это не настоящий параллелизм. Он просто откладывает вызов метода, но не делает то, что, по вашему мнению, делает. Вместо этого, если вы хотите выполнять эти методы параллельно, используйте Task, ThreadPool или Thread.

ОБНОВИТЬ

Вы также можете использовать параллельный цикл TPL для цикла, который может быть ближе к тому, чего вы изначально надеялись достичь:

Parallel.ForEach(anObjectList, anObjectItem => {
    // do something with anObjectItem
});
// this parallelizes the for-loop iterations

ОБНОВЛЕНИЕ 2

Вот как выполнить задачи, используя рабочие потоки из ThreadPool и ManualResetEvent.

ManualResetEvent mreComplete = new ManualResetEvent(false);
int callsRemaining;

GetMergeSectionCaller caller = new GetMergeSectionCaller(GetMergeSection);
callsRemaining = anObjectList.Count;
mreComplete.Reset();
foreach (var r in anObjectList) {
    ThreadPool.QueueUserWorkItem((Action)delegate {
        caller(r.ToString());
        lock{
           if(--callsRemaining==0) mreComplete.Set();
        }
    }
}
mreComplete.Wait();
Другие вопросы по тегам