使用async时出现意外结果等待

时间:2016-03-04 18:39:53

标签: c# asynchronous async-await

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

        var result = a.Method();

        Console.Read();
    }
}

class A
{
    public async Task<string> Method()
    {
        await Task.Run(new Action(MethodA));
        await Task.Run(new Action(MethodB));

        return "done";
    }

    public async void MethodA()
    {
        for (var i = 0; i < 10; i++)
        {
            Console.WriteLine("MethodA" + i);
            await Task.Delay(1000);
        }
    }

    public async void MethodB()
    {
        for (var i = 0; i < 10; i++)
        {
            Console.WriteLine("MethodB" + i);
            await Task.Delay(1000);
        }
    }
}

Result Output

enter image description here

我认为这里的期望是首先输出MethodA1,MethodA2 ... MethodA9然后开始输出MethodB1 ... MethodB9。

但似乎不是。有人对此有解释吗?谢谢!

编辑:

感谢您的回复。实际上我知道如何输出正确的结果,但只是想知道为什么我的代码表现得很奇怪,因为我认为它违背了我对async / await的了解。

class A
{
    public async Task<string> Method()
    {
        await Task.Run(new Action(MethodA));

        await Task.Run(async () => {
            for (var i=0;i<10;i++)
                await Task.Delay(100);
            Console.WriteLine("should output before done");
        });

        return "done";
    }

    public async void MethodA()
    {
        var returnString = string.Empty;
        for (var i = 0; i < 10; i++)
        {
            await Task.Delay(200);
        }

        Console.WriteLine("MethodA " + Thread.CurrentThread.ManagedThreadId);

    }
}

我简化了代码,现在它就像这样。第一个等待快速转移控制到下一个等待,然后第二个等待表现符合我的预期和&#34;完成&#34;终于输出了。但我不知道是什么造就了这一点。

Result Output

enter image description here

更新:请记住示例代码是一种不好的做法。你可以read this post for more information

一位朋友表示不同之处在于

await Task.Run(new Action(MethodA));

不等于

 await Task.Run(async () => {
            for (var i=0;i<10;i++)
                await Task.Delay(100);
            Console.WriteLine("should output before done");
        });

前者引用了方法A&#39;以同步方式,而海报一是正确的异步调用。但它仍然让我困惑,因为如果这是真的那么异步动作可以并且只能由lambda表达式表示。我的意思是,没有像新的异步行动(...)&#39;正确?

更新

我尝试了Task.Factory.StartNew(Action动作),它被认为等同于Task.Run(动作动作)并且事情再次发生变化:

class A
{
    public async Task<string> Method()
    {
        await Task.Factory.StartNew(new Action(MethodA));

        await Task.Factory.StartNew(async () => {
            for (var i=0;i<10;i++)
                await Task.Delay(100);
            Console.WriteLine("should output before done");
        });

        return "done";
    }

    public async void MethodA()
    {
        for (var i = 0; i < 10; i++)
            await Task.Delay(200);
        Console.WriteLine("MethodA");

    }
}

我想我需要更多的帮助...

3 个答案:

答案 0 :(得分:7)

您的代码行为原因是因为您启动调用其中一个方法的任务将在方法中的第一个async void完成,因为它是await Task.Run(new Action(MethodA));并且只是继续因此导致当MethodA正文仍在运行时,async void将控件返回给线程。这是因为class A { public async Task<string> Method() { // You create a task to run MethodA await Task.Run(new Action(MethodA)); // control returns here on the first `await` in MethodA because // it is `async void`. return "done"; } public async void MethodA() { for (var i = 0; i < 10; i++) { Console.WriteLine("MethodA" + i); // Here the control is returned to the thread and because // it's `async void` an `await` will only block until you reach // this point the first time. await Task.Delay(1000); // } } } 基本上是火和忘记。

以下是您的代码的简化版本中发生的情况。

Task

要获得您想要的结果,首先需要从方法中返回Task.Run,以便等待它们。那么你也不需要class A { public async Task<string> Method() { await MethodA(); await MethodB(); return "done"; } public async Task MethodA() { for (var i = 0; i < 10; i++) { Console.WriteLine("MethodA" + i); await Task.Delay(1000); } } public async Task MethodB() { for (var i = 0; i < 10; i++) { Console.WriteLine("MethodB" + i); await Task.Delay(1000); } } }

import android.app.Activity;
import android.os.Bundle;

public class FifthActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fifth_layout);
    }
}

答案 1 :(得分:0)

您可能希望从MethodA和MethodB返回Task,而不是在这些方法中写入控制台。像这样:

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

        var result = a.Method();

        Console.Read();
    }
}

class A
{
    public async Task<string> Method()
    {
        Console.WriteLine(await MethodA());
        Console.WriteLine(await MethodB());

        return "done";
    }

    public async Task<string> MethodA()
    {
        var returnString = string.Empty;
        for (var i = 0; i < 10; i++)
        {
            returnString += "MethodA" + i + '\n'+'\r';
            await Task.Delay(100);
        }
        return returnString;
    }

    public async Task<string> MethodB()
    {
        var returnString = string.Empty;
        for (var i = 0; i < 10; i++)
        {
            returnString += "MethodB" + i + '\n'+'\r';
            await Task.Delay(100);
        }
        return returnString;
    }
}

注意:我没有测试过该代码,但它应该说明一般的想法。

答案 2 :(得分:-1)

async + await != sync,因为延续

有关详细信息,请Asynchronous Programming with Async and Await (C# and Visual Basic)

Async methods旨在成为非阻止操作。

正在等待的任务正在运行时

An await expression in an async method doesn’t block the current thread。相反,表达式将方法的其余部分作为延续进行注册,并将控制权返回给async方法的调用者。