1

所以我想在這裏做的是:如何正確使用異步,等待和ManualResetEvents控制的無限while循環

  1. 使發動機迴路和工作對象上,如果隊列不爲空。
  2. 如果隊列爲空,我調用manualresetevent使線程休眠。
  3. 當一個項目被添加,並且循環未激活時,我設置manualresetevent。
  4. 爲了讓它更快,我從列表中選取最多5個項目,並對它們進行異步操作,並等待所有項目完成。

問題:

  1. 在兩個列表中明確方法是儘快到AddToUpdateQueueMethod一個新的呼叫被稱爲調用。
  2. 因爲我在等待Task.WhenAll(任務),所以線程應該在繼續前等待它的完成,因此清單應該只在Task.WhenAll(任務)返回後被調用。

我在這裏錯過了什麼,或者什麼是更好的實現方法。

public async Task ThumbnailUpdaterEngine() 
    { 
     int count; 
     List<Task<bool>> tasks = new List<Task<bool>>(); 
     List<Content> candidateContents = new List<Content>(); 
     while (true) 
     { 

      for (int i = 0; i < 5; i++) 
      { 
       Content nextContent = GetNextFromInternalQueue(); 
       if (nextContent == null) 
        break; 
       else 
        candidateContents.Add(nextContent); 

      } 

      foreach (var candidateContent in candidateContents) 
      { 
       foreach (var provider in interactionProviders) 
       { 
        if (provider.IsServiceSupported(candidateContent.ServiceType)) 
        { 
         Task<bool> task = provider.UpdateThumbnail(candidateContent); 
         tasks.Add(task); 
         break; 
        } 
       } 
      } 
      var results = await Task.WhenAll(tasks); 
      tasks.Clear(); 
      foreach (var candidateContent in candidateContents) 
      { 
       if (candidateContent.ThumbnailLink != null && !candidateContent.ThumbnailLink.Equals(candidateContent.FileIconLink, StringComparison.CurrentCultureIgnoreCase)) 
       { 
        Task<bool> task = DownloadAndUpdateThumbnailCache(candidateContent); 
        tasks.Add(task); 
       } 
      } 
      await Task.WhenAll(tasks); 

      //Clean up for next time the loop comes in. 
      tasks.Clear(); 
      candidateContents.Clear(); 

      lock (syncObject) 
      { 
       count = internalQueue.Count; 
       if (count == 0) 
       { 
        isQueueControllerRunning = false; 
        monitorEvent.Reset(); 
       } 
      } 
      await Task.Run(() => monitorEvent.WaitOne()); 


     } 
    } 

    private Content GetNextFromInternalQueue() 
    { 
     lock (syncObject) 
     { 
      Content nextContent = null; 
      if (internalQueue.Count > 0) 
      { 
       nextContent = internalQueue[0]; 
       internalQueue.Remove(nextContent); 
      } 
      return nextContent; 
     } 
    } 

    public void AddToUpdateQueue(Content content) 
    { 
     lock (syncObject) 
     { 
      internalQueue.Add(content); 
      if (!isQueueControllerRunning) 
      { 
       isQueueControllerRunning = true; 
       monitorEvent.Set(); 
      } 
     } 
    } 
+6

隨着TPL,你幾乎永遠不會需要使用'ManualResetEvent'以及諸如此類的東西,比如'Task.Run(()= > monitorEvent.WaitOne())',除非你處理一些遺留代碼。看看TPL Dataflow,它爲組織流水線處理提供了很多東西。 – Noseratio 2015-02-12 04:48:22

+0

您正在尋找的是['BlockingCollection '](https://msdn.microsoft.com/en-us/library/dd267312%28v=vs.110%29.aspx) – 2015-02-12 06:46:51

回答

1

您應該簡單地使用TPL Dataflow。它是TPL之上的演員框架,支持async。使用一個ActionBlockasync動作和MaxDegreeOfParallelism 5:

var block = new ActionBlock<Content>(
    async content => 
    { 
     var tasks = interactionProviders. 
      Where(provider => provider.IsServiceSupported(content.ServiceType)). 
      Select(provider => provider.UpdateThumbnail(content)); 
     await Task.WhenAll(tasks); 

     if (content.ThumbnailLink != null && !content.ThumbnailLink.Equals(
      content.FileIconLink, 
      StringComparison.CurrentCultureIgnoreCase)) 
     { 
      await DownloadAndUpdateThumbnailCache(content); 
     } 
    }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 5}); 

foreach (var content in GetContent()) 
{ 
    block.Post(content); 
} 

block.Complete(); 
await block.Completion 
+0

因此,這將很重要while循環的內部結構,但是如何在隊列爲空時使while循環進入睡眠狀態,並且只有在隊列有某些時纔會喚醒。我可以使用await Task.Sleep(someTimeout),但這不是最優的。除了塊初始化之外,上面的代碼將在while循環內部進行,但是如何控制while循環線程。 – 2015-02-12 17:58:56

+0

@ Jack_2060你不需要。 TPL Dataflow爲您處理所有這些事情。它有5個內部運行內容的任務,當它們完成並且隊列爲空時,任務完成。下次您發佈到該塊時,將創建一個新任務,等等。 – i3arnon 2015-02-12 19:12:46