2017-01-10 321 views
2

我很努力地掌握c#異步等待的基本概念。運行異步Foreach循環C#異步等待

基本上我有一個需要處理的對象列表,處理過程涉及迭代其屬性並加入字符串,然後創建一個新對象(在本例中稱爲trellocard),並最終添加一個trellocards。

迭代需要很長時間,所以我想要做的是異步處理多個對象。

我試過多種方法,但基本上我想要做這樣的事情。 (在下面的例子中,我已經刪除了處理,只是把system.threading.thread.sleep(200)。Im等待這不是一個異步方法,我可以使用tasks.delay,但重點是我的處理不有任何異步方法,我想只是多個實例運行整個方法。

private async Task<List<TrelloCard>> ProcessJobs(IQueryable<IGrouping<CardGrouping, Job>> jobs) 
    { 
     List<TrelloCard> cards = new List<TrelloCard>(); 

     foreach (var job in jobs.ToList()) 
     { 
      card = await ProcessCards(job, cards); // I would like to run multiple instances of the processing 
      cards.add(card); //Once each instance is finshed it adds it to the list 
     } 


    private async Task<TrelloCard> ProcessCards(Job job) 
    { 
     System.Threading.Thread.Sleep(2000); //Just for examples sake 

     return new TrelloCard(); 
    } 

回答

1

我很努力掌握C#異步等待的基本概念。

簡單的定義是, Async-Await是.Net併發性的一部分,可用於進行多個IO調用,並且不會浪費線程,這些線程用於計算操作。 TS像調用數據庫,Web服務,網絡電話,文件IO,所有這一切並不需要當前進程線程

在當前的情況下,如果用例是:

  1. 通過其迭代性能和連接字符串,然後創建一個新的對象
  2. 最終加入trellocards

這似乎是一個計算綁定操作,直到和除非你正在做一個IO的名單,對我來說,看來你是遍歷一個內存對象,對於這種情況更好的選擇是:

  1. Parallel.ForEach,並行的內存中處理,但你必須要小心的競爭條件,作爲一個給定的內存可以被多個線程訪問,因此特地寫操作過程中破壞它,所以至少從System.Collections.Concurrent命名空間,或當前代碼使用線程安全的集合像ConcurrentBag不斷適應使用情況,而不是List<TrelloCard>,或者你可以考慮以下Thread safe list

還要注意的是,如果你的方法不默認Async,那麼你可能會計劃在包裝他們在一個Task.Run,以await,儘管這將需要一個線程池線程,但可以使用Async-Await

Parallel.Foreach代碼爲你的使用情況被稱爲(我做的直接替代品,似乎是一個問題你的代碼,因爲ProcessCards功能,只是需要招聘對象,但你也通過收集Cards,這是編譯錯誤):

private List<TrelloCard> ProcessJobs(IQueryable<IGrouping<CardGrouping, Job>> jobs) 
    { 
     ConcurrentBag<TrelloCard> cards = new ConcurrentBag<TrelloCard>(); 

     Parallel.ForEach(jobs.ToList(), (job) => 
     { 
      card = ProcessCards(job); // I would like to run multiple instances of the processing 
      cards.Add(card); //Once each instance is finshed it adds it to the list 
     }); 

      return cards.ToList(); 
    } 

    private TrelloCard ProcessCards(Job job) 
    { 
     return new TrelloCard(); 
    } 
+0

謝謝!它令人驚歎,我一直在尋找網絡,你的第一個3行給了我燈泡的時刻。現在對我來說更有意義。 – michael

+0

歡迎,對於一個web場景,他們就像一個Ajax調用,具有一些細微的差異,這是.Net特定的 –

+0

Down投票者護理解釋請 –

2

如果你希望它們並行運行,你可以生成一個新的任務爲每個操作然後等待所有使用Task.WhenAll的完成。

private async Task<List<TrelloCard>> ProcessJobs(IQueryable<IGrouping<CardGrouping, Job>> jobs) 
{ 
    List<Task<TrelloCard>> tasks = new List<Task<TrelloCard>>(); 

    foreach (var job in jobs) 
    { 
     tasks.Add(ProcessCards(job)); 
    } 

    var results = await Task.WhenAll(tasks); 

    return results.ToList(); 
} 


private Task<TrelloCard> ProcessCards(Job job) 
{ 
    return Task.Run(() => 
    { 
     System.Threading.Thread.Sleep(2000); //Just for examples sake 

     return new TrelloCard(); 
    }); 
} 
+0

只有你可能想避免在這裏是'jobs.ToList()',因爲它不再是遠程操作,所有數據都將加載到進程內存中,因此計算限制操作比IO限制操作更合適。它實際上會打敗Async-Await的目的。 –

+0

@MrinalKamboj是編寫此代碼(計算綁定)與異步/等待的唯一原因是如果它是WPF/WinForms並將阻止用戶界面。在ASP.NET應用程序中,計算密集型工作不應使用異步/等待。 –

+0

即使在WPF中,你也可以在'Threadpool'線程上啓動一個調用,而不需要'Async-Await',除非你打算更新一個總是需要完成的'Ui control'在Ui線程上下文中,或者是一個異常(使用Async-Await很容易實現,因爲沒有線程)。理想情況下,沒有計算密集型工作的應用程序邏輯應使用僅爲IO調用設計的Async-Await。 –

1

jobs.ToList()只是在浪費記憶。它已經是IEnumerable因此可以在foreach中使用。

ProcessCards不能編譯。你需要像這樣

private Task<TrelloCard> ProcessCards(Job job) 
    { 
     return Task.Run(() => 
     { 
      System.Threading.Thread.Sleep(2000); //Just for examples sake 

      return new TrelloCard(); 
     }); 
    } 

現在你要ProcessJobs

  • 爲每個作業ProcessCards任務
  • 等待所有任務完成
  • 回報TrelloCard

    序列
    private async Task<List<TrelloCard>> ProcessJobs(IQueryable<IGrouping<CardGrouping, Job>> jobs) 
    { 
        return await Task.WhenAll(jobs.Select(ProcessCards)); 
    } 
    
+0

+1,正如我在前面的一個註釋中指出的那樣,對於'Async-Await'使用它的重要性不在於'jobs.ToList()',因爲一旦它在本地內存中購買,它可以是簡單的並行處理而不是異步處理 –