2014-10-04 75 views
1

一個小程序,我創建了理解異步等待的工作,並呼籲在for循環中的異步方法,作爲一個直接的方法調用:異步等待 - 直接的方法調用VS任務包​​裝呼叫

sumProgram.CallSum(i, i + 1); 

或使用任務API Task.Run/Task.Factory.StartNew

我最初的理解是任務API會使它快得多,但與我的預期相反,這肯定反映了我的理解不足,直接調用在性能方面要好得多。實際上,當在GetSum方法中引入額外的線程休眠時,它似乎隻影響任務調用,並且也會大幅度影響。

現在我明白了直接調用的第一部分速度更快,因爲它們是異步執行的,沒有任何額外的任務和讓它們等待的開銷,但是當我將相同的技術轉移到真正的編程時範式,那麼問題是:

  • 對於直接呼叫,沒有什麼模仿Task.WaitAll,所以他們會退出調用的方法,即使所有的執行是不完整的,所以是我唯一的選擇任務的包裝。

  • 我會得到令人費解的結果,因爲在直接調用秒錶將發佈之前就全部執行完成時間,並且不會對任務包裝的情況下,由於爲WaitAll

  • 對於執行該程序,你需要註釋/取消對相關部分進行正確的結果

    class Program 
        { 
         static void Main(string[] args) 
         { 
          Program sumProgram = new Program(); 
    
         List<Task> taskList = new List<Task>(); // Only For Task 
    
         Stopwatch sw = Stopwatch.StartNew(); 
    
         for (int i = 0; i < 100000; i++) 
         { 
          taskList.Add(Task.Factory.StartNew(() => { sumProgram.CallSum(i, i + 1); })); // For Task use one 
          taskList.Add(Task.Run(() => { sumProgram.CallSum(i, i + 1); })); // For Task use one 
          sumProgram.CallSum(i, i + 1); 
         } 
    
         Task.WaitAll(taskList.ToArray()); // Only For Task 
    
         sw.Stop(); 
         Console.WriteLine("Time Elapsed :: " + sw.ElapsedMilliseconds); 
        } 
    
        public async Task CallSum(int num1, int num2) 
        { 
         Func<int> callFunc = (() => 
         { 
          return GetSum(num1, num2); 
         }); 
    
         int result = await Task.Run<int>(callFunc); 
         //Console.WriteLine("Sum Result :: " + result); 
        } 
    
        public int GetSum(int num1, int num2) 
        { 
         Thread.Sleep(10); 
         return (num1 + num2); 
        } 
    } 
    
+3

任務創建和處理有大量開銷。這些任務做得太少而不重要。 – 2014-10-04 11:04:34

+2

實際上你的問題是什麼? – i3arnon 2014-10-04 11:12:25

+0

有些問題需要重申 - 在真正的編程場景中,似乎沒有辦法等待直接調用的異步執行,所以帶有等待的任務似乎只能在所有異步執行返回之前保持該方法,還有其他選項對我來說 – 2014-10-04 11:36:26

回答

1

您的代碼的最大問題是您正在使用的選項的節點正在等待CallSum()完成。要做到這一點,請參照CallSum()返回的Task並等待它。例如:

taskList.Add(sumProgram.CallSum(i, i + 1)); 
+0

感謝您的糾正,我已經意識到這個問題,並且這種改變似乎表現得更好。直接調用當然不是解決方案,因爲我不能等待異步任務完成。你是否認爲Parallel api在這種情況下表現會更好,因爲我需要等待並行/異步任務完成 – 2014-10-04 18:16:02

2

的上述評論是說,你有太多的小作品裏面的任務正在做它以超過它的安裝成本。

另外,任務和異步等待不適用於直接調用同樣簡單的情況。使某些異步操作不會加快速度。它們用於在等待冗長/可失敗/可取消任務的情況下讓您的調用線程繼續運行/執行其他任務,以便以硬件實際並行化的方式完成或分配各個內核的足夠工作。並且完成所有這些工作,而不需要難以辨認的異步編程代碼。

你的實驗很簡單,是錯誤的。你所測量的是在無法獲勝的情況下,使用.NET中的任務機制。

試試這個。在每個任務中,將一些內存從一個位置複製到另一個位置(使用Marshal.AllocHGlobal進行分配並使用P/Invoke CopyMemory進行復制)。一旦你達到了臨界值,你就會看到這個機制與單線程拷貝相比要快多少。此外,使用異步等待您的代碼將看起來更好。

+0

完全同意,我知道建議Task需要做很多精力充沛的工作,爲任務顯示好處。樣本不匹配,我的問題雖然沒有什麼不同。在真正的程序中,即使我有一個直接調用工作更快,但似乎沒有辦法阻止執行,除了使用任務,我沒有別的選擇。在實際情況下,每個任務都需要進行密集的數據庫調用,需要並行執行 – 2014-10-04 17:00:53

5

async!=「更快」

實際上,async代碼幾乎總是(稍微)慢一點。

那麼,爲什麼要用async

在客戶端(UI)應用程序中,async允許您保持對用戶的響應。對比此代碼:

void Button_Click() 
{ 
    Thread.Sleep(10000); 
} 

與此代碼:

async void Button_Click() 
{ 
    await Task.Delay(10000); 
} 

在服務器端(例如,ASP.NET),async允許你的代碼使用較少的線程來服務更多的請求。因此,增加了可擴展性。

請注意,在這兩種情況下,async代碼實際上是比其同步副本更慢。對於UI,睡眠當前線程比創建和啓動計時器更快,創建並等待任務,然後在計時器觸發時恢復異步方法。對於ASP.NET,阻止當前線程比爲異步操作創建任務要快,然後在異步方法恢復時將請求上下文切換到另一個線程。

但是,async帶來其他好處。對於UI,UI線程在延遲期間不會被阻塞。對於ASP.NET,當異步操作正在進行時,請求線程可以自由處理其他請求。

async不是關於速度;它是關於釋放當前線程。

+0

感謝您提供了很好的信息,我能夠理解我對於該概念的理解上的缺陷 – 2014-10-04 18:18:00