c# - 使用异步等待时出现意外结果

标签 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);

    }
}

我简化了代码,现在是这样的。第一个 await 快速将控制转移到下一个 await,然后第二个 await 的行为符合我的预期,最后输出“完成”。但我不知道是什么造成了这里的差异。

Result Output

enter image description here

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

和 friend 建议的区别在于

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");
        });

前者以同步方式调用“methodA”,而海报者是正确的异步调用。但这仍然让我感到困惑,因为如果这是真的,那么异步操作可以而且只能由 lambda 表达式表示。我的意思是,没有像“new async Action(...)”这样的东西吧?

更新:

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

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");

    }
}

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

最佳答案

你的代码表现如此的原因是因为你开始调用其中一个方法的任务将在方法中的第一个 await 完成,因为它是 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);
        }
    }
}

关于c# - 使用异步等待时出现意外结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35803629/

相关文章:

c# - 尝试使用异步方法访问 Thread.CurrentPrincipal 时出现 ObjectDisposedException

c# - 在 ThreadPool 线程之间传递数据

c# - 如何检查进程是否没有响应?

c# - 如何区分用户启动的进程和系统启动的进程?

c# - IF FALSE THEN ... END IF 中的包装代码在 VBScript 中有意义吗?

javascript - Knex 中的链接查询/事务?

c# - 异步进程启动并等待它完成

c# - ontextchanged 异步 sql 存储过程调用

c# - VB.Net 内联注释

c# - 使用 async/await 的技巧