C#异步等待怪异行为

问题描述:

我是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

正因为如此:

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; *调用者*应该决定是否推送到线程池)。 –

我怀疑意图是使所有三个任务做的工作并行。这是如何可以实现的选项之一:

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

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