1

背景信息:我正在嘗試創建一個能夠同時處理5個併發操作的單例類。每個操作由SomeAsyncMethod表示。ContinueWith任務完成前任務運行


該方法存在於單例類中。

consumersConcurrentDictionary<int,Task>

我的問題:由於某種原因,ContinueWith委託SomeAsyncMethod完成之前運行。我知道這種情況發生的原因是因爲我有另一種方法,手錶instance.Consumers.Count - 在SomeAsyncMethod完成之前計數爲0

這是爲什麼?

public bool TryAddDequeueRequest() 
    { 
     if (instance.Consumers.Count < 5) 
     { 
      Task bogusTask; 
      Task newTask = new Task(SomeAsyncMethod); 

      //RUNS AFTER THE REQUEST IS COMPLETED 
      newTask.ContinueWith(t => 
      { 
       instance.Consumers.TryRemove(t.Id, out bogusTask); 
      }); 

      //WE ADD THE TASK TO QUEUE 
      instance.Consumers.TryAdd(newTask.Id, newTask); 

      //SET IT AND FORGET IT 
      newTask.Start(); 



      return true; 
     } 
     else 
      return false; 
    } 
+0

你怎麼知道它尚未完成? – 2014-09-05 20:20:52

+0

添加了詳細信息。 (我知道發生這種情況的原因是因爲我有另一種方法來監視instance.Consumers.Count - 在SomeAsyncMethod完成之前計數爲0.) – 2014-09-05 20:21:20

+0

'SomeAsyncMethod'調用某些服務並更新數據庫。全部通過'async'和'await'操作完成。 – 2014-09-05 20:22:29

回答

2

SomeAsyncMethod,如果它的名稱的任何跡象,是一種異步方法,大概一個返回一個Task。你正在創建一個新的任務start這個異步操作在另一個線程中。那Task會在你完成時返回開始的異步操作,而不是當它啓動的異步操作完成時。

雖然你可以解開任務,但更簡單的選擇是不把它包裹在第一位。呼籲通過異步方法返回的Task延續:

SomeAsyncMethod().ContinueWith(t => 
{ 
    instance.Consumers.TryRemove(t.Id, out bogusTask); 
}); 
instance.Consumers.TryAdd(newTask.Id, newTask); 

當然,如果你希望能夠用一個固定的平行度進行異步操作的一些數量,有更簡單的方法。您可以使用SemaphoreSlim公平平凡創建任何固定的平行度的工作隊列:

public class FixedParallelismQueue 
{ 
    private SemaphoreSlim semaphore; 
    public FixedParallelismQueue(int maxDegreesOfParallelism) 
    { 
     semaphore = new SemaphoreSlim(maxDegreesOfParallelism); 
    } 

    public async Task<T> Enqueue<T>(Func<Task<T>> taskGenerator) 
    { 
     await semaphore.WaitAsync(); 
     try 
     { 
      return await taskGenerator(); 
     } 
     finally 
     { 
      semaphore.Release(); 
     } 
    } 
    public async Task Enqueue(Func<Task> taskGenerator) 
    { 
     await semaphore.WaitAsync(); 
     try 
     { 
      await taskGenerator(); 
     } 
     finally 
     { 
      semaphore.Release(); 
     } 
    } 
} 
+0

啊!讓我嘗試一下。 – 2014-09-05 20:31:22

+0

SephamoreSlim塊。 '當計數到零時,對其中一個Wait方法的後續調用會阻塞,直到其他線程釋放信號爲止。# – 2014-09-05 20:38:18

+2

@SimchaKhabinsky好東西我沒有叫'Wait',那不是。這段代碼不會阻塞,它完全是異步的。 – Servy 2014-09-05 20:39:03

0

由於SomeAsyncMethod是異步的,它完成了自己的任務前返回。

如果你有以上的SomeAsyncMethod代碼控制,然後重構它是同步的(沒有await/async),或者如果已經有一個非異步版本,那麼就使用它。

如果你沒有在該方法的代碼控制,你可以等待它周圍的任務,然後再繼續完成:

Task newTask = new Task(()=>{ SomeAsyncMethod().Wait(); }); 
+1

解決方法是*不將它包裝在任務中*不安排線程池線程啓動任務,同步等待它,然後設置任務的結果。這太愚蠢了。 – Servy 2014-09-05 20:50:27

+0

我已經更新了我的答案。 – 2014-09-05 20:53:17

+1

@MarkCidade您修改後的建議同樣不靈敏。爲什麼你會想要使用操作的同步版本,並創建一個新線程來坐在那裏等待它,而不是使用操作的固有異步實現?它避免了坐在那裏無所事事的需要。解決方案是不將任務包裝在另一個任務中,而不是找到使異步代碼同步的方法。 – Servy 2014-09-05 21:02:56