2017-08-02 88 views
0

我想使用Parallel.ForEach添加1000多個任務。以下代碼用於發送電子郵件通知。問題是,它只適用於約150〜200通知&我收到電子郵件,但在此之後代碼被凍結&沒有收到電子郵件。Parallel.ForEach在一段時間後會凍結

有人可以請指導我在正確的方向。

var exceptions = new ConcurrentQueue<Exception>(); 

try 
{ 
    List<ParallelWorker_EmailNotification> workers = new List<ParallelWorker_EmailNotification>(); 

    foreach (Email mail in listEmails) 
    { 
     workers.Add(new ParallelWorker_EmailNotification(mail)); 
    } 

    Parallel.ForEach(workers, async worker => 
    { 
     try 
     { 
      await worker.SendNotification(); 
     } 
     catch (Exception ex) 
     { 
      exceptions.Enqueue(ex); 
     } 
    }); 
} 
catch (Exception ex) 
{ 
    exceptions.Enqueue(ex); 
} 
+0

從ParallelWorker_EmailNotification添加代碼 – Gusman

回答

2

Parallel.ForEach不傳入異步函數工作的async worker =>方法簽名是async void而且有可能你的問題的根源。 Parallel.ForEach我們解鎖,因爲它認爲工作已完成,但工作仍在後臺處理,這就是爲什麼你看不到項目處理。

最簡單的解決方案(如果SendNotification是一個合適的異步函數)只是選擇項目並將所有任務都放到一個IEnumerable中並等待它們。

var exceptions = new ConcurrentQueue<Exception>(); 

    try 
    { 
     var tasks = listEmails.Select(mail => new ParallelWorker_EmailNotification(mail)) 
           .Select(async worker => 
      { 
       try 
       { 
        await worker.SendNotification(); 
       } 
       catch (Exception ex) 
       { 
        exceptions.Enqueue(ex); 
       } 
      }); 
     await Task.WhenAll(tasks); 
    } 
    catch (Exception ex) 
    { 
     exceptions.Enqueue(ex); 
    } 

如果SendNotification是一個函數,需要一些時間它產生控制之前返回給調用者的最佳解決方案是使用用於TPL Dataflow的處理。

var exceptions = new ConcurrentQueue<Exception>(); 

    try 
    { 
     var actionBlock = new ActionBlock<ParallelWorker_EmailNotification>(async worker => 
      { 
       try 
       { 
        await worker.SendNotification(); 
       } 
       catch (Exception ex) 
       { 
        exceptions.Enqueue(ex); 
       } 
      }, new ExecutionDataflowBlockOptions 
        { 
        MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded       
        } 
      ); 

     foreach (Email mail in listEmails) 
     { 
      actionBlock.Post(new ParallelWorker_EmailNotification(mail)); 
     } 
     actionBlock.Complete(); 
     actionBlock.Completion.Wait(); 
    } 
    catch (Exception ex) 
    { 
     exceptions.Enqueue(ex); 
    } 
+0

TPL Dataflow示例很不錯。有沒有辦法代碼不必等待「actionBlock.Completion.Wait();」並移動到下一批,但確保前一批完成? –

+0

在某些時候,您必須「等待並檢查」才能看到工作已經完成,您要在何處進行該檢查取決於您。如果你想從'actionBlock.Completion'返回任務,你可以讓調用者每次批量調用一次你的函數,一旦所有的批次都完成了'Task.WaitAll(batchTasks)'或'await Task.WhenAll(batchTasks )'在所有返回的任務上。 –

相關問題