2016-06-10 215 views
0

我是C#的新手,現在我想了解異步/等待特徵。所以,我創建了小沙盒應用程序:C#異步等待怪異行爲

namespace sandbox 
{ 
public class Test 
{ 
    public async Task<string> GetItemsAsync() 
    { 
     var a = await Task1(); 
     var b = await Task2(); 
     var c = await Task3(); 

     return a + b + c; 
    } 

    public string GetItems() 
    { 
     return _T1() + _T2() + _T3(); 
    } 

    private readonly int cycles = 100000000; 

    private async Task<string> Task1() 
    { 
     return await Task.Factory.StartNew(_T1); 
    } 

    private async Task<string> Task2() 
    { 
     return await Task.Factory.StartNew(_T2); 
    } 

    private async Task<string> Task3() 
    { 
     return await Task.Factory.StartNew(_T3); 
    } 

    // long running operation 
    private string _T1() 
    { 
     for (int i = 0; i < cycles; i++) ; 
     for (int i = 0; i < cycles; i++) ; 
     return "One"; 
    } 

    // long running operation 
    private string _T2() 
    { 
     for (int i = 0; i < cycles; i++) ; 
     for (int i = 0; i < cycles; i++) ; 
     return "Two"; 
    } 

    // long running operation 
    private string _T3() 
    { 
     for (int i = 0; i < cycles; i++) ; 
     for (int i = 0; i < cycles; i++) ; 
     return "Three"; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var t = new Test(); 

     Console.WriteLine("Async"); 
     Stopwatch sw = new Stopwatch(); 
     sw.Start(); 
     var result = t.GetItemsAsync(); 
     Console.WriteLine(result.Result); 
     sw.Stop(); 
     Console.WriteLine(sw.ElapsedMilliseconds); 

     Console.WriteLine("Sync"); 
     sw.Restart(); 
     var strResult = t.GetItems(); 
     Console.WriteLine(strResult); 
     sw.Stop(); 
     Console.WriteLine(sw.ElapsedMilliseconds); 

     Console.ReadLine(); 
    } 
} 
} 

但結果是怪異:

Async 
OneTwoThree 
1754 
Sync 
OneTwoThree 
1506 

異步方法運行時間比類似的同步之一。對我來說,它看起來像異步方法同步運行,但我不知道爲什麼。

+3

因爲你等待所有人同步。 – Liam

+1

通過等待任務的您正在將其從異步更改爲同步,await關鍵字將暫停當前任務並且不會繼續,直到等待完成的任務完成 – MikeT

+0

使用await將使代碼同步運行並且還會增加開銷創建一個狀態機(每次使用'await'),所以它的運行速度也比同步運行慢。 – Igor

回答

4

正因爲如此:

var a = await Task1(); 
var b = await Task2(); 
var c = await Task3(); 

之前,你甚至開始Task2你已經等了Task1完成。所以你不是在平行運行它們,而是依次運行它們。

如果要啓動所有3個任務,然後等待它們完成,你就必須改變這種方法是:

public async Task<string> GetItemsAsync() 
{ 
    var t1 = Task1(); 
    var t2 = Task2(); 
    var t3 = Task3(); 

    var a = await t1; 
    var b = await t2; 
    var c = await t3; 

    return a + b + c; 
} 

或者只是最後一部分:

return (await t1) + (await t2) + (await t3); 

另外,這個代碼是一個反模式:

private async Task<string> Task3() 
{ 
    return await Task.Factory.StartNew(_T3); 
} 

你不需要async/await這裏因爲... e 在您的子任務返回後,您無法繼續使用此方法。

而不是簡單地改寫這個方法(和它的兄弟姐妹),以這樣的:

private Task<string> Task3() 
{ 
    return Task.Factory.StartNew(_T3); 
} 
+0

「未開始任務」是什麼意思?這是3種方法的責任,明確地開始新的任務。開始任務不是我的責任*如果該任務返回給我*。這是*的責任,無論它是什麼返回*。無論如何,這些任務顯然開始,'Task.Factory.StartNew'。 –

+0

道歉我誤解他們的代碼,並沒有注意到任務方法內的開始 – MikeT

+1

op *可以* elide'async' /'await';儘管如此,我還沒有把它稱爲反模式。什麼是*絕對*反模式是使用'StartNew'而不指定任務調度器(應該使用'Task.Run'來代替)。另外,你可以做一個強大的例子,返回一個'StartNew' /'Run'任務也是一個反模式(同步代碼應該有同步API; *調用者*應該決定是否推送到線程池)。 –

0

我懷疑意圖是使所有三個任務做的工作並行。這是如何可以實現的選項之一:

public async Task<string> GetItemsAsync() 
{ 
    var a = Task1(); 
    var b = Task2(); 
    var c = Task3(); 

    return string.Concat(await Task.WhenAll(a, b, c)); 
}