繁体   English   中英

控制台应用程序中的 C# async/await

[英]C# async/await in a console application

我想在 C# 控制台应用程序中测试 async 和 await,所以我编写了这个测试代码。 当我在 Winforms 应用程序中使用 async/await 时,例如:在 ButtonClicked 事件处理函数中调用这个 doSth。 它会在另一个线程中延迟 3000 毫秒,并在 MainThread 中输出“doSth execute finished”。 但是在这个控制台应用程序中,它在工作线程中输出“doSth execute finished”,为什么?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp12
{
    class Program
    {
        static void Main(string[] args)
        {
            doSth();
            Console.WriteLine("doSth call finished.");

            Thread.Sleep(5000);

            Console.WriteLine("Main finished.");
        }

        private static async void doSth()
        {
            Console.WriteLine("doSth execute Started...");

            await Task.Delay(3000);

            Console.WriteLine("doSth execute finished.");
        }
    }
}

这是 Winforms 应用程序中的测试代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp4
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            doSth();
            Console.WriteLine("doSth call finished.");

            Thread.Sleep(5000);

            Console.WriteLine("Main finished.");
        }

        private static async void doSth()
        {
            Console.WriteLine("doSth execute Started...");

            await Task.Delay(3000);

            Console.WriteLine("doSth execute finished.");
        }
    }
}

这是控制台应用程序中的堆栈跟踪 这是控制台应用程序中的堆栈跟踪,最终输出是:

doSth execute Started...
doSth call finished.
doSth execute finished.
Main finished.
请按任意键继续. . .

这是 Winforms 应用程序中的堆栈跟踪 这是 Winforms 应用程序中的堆栈跟踪,输出为:

doSth execute finished.
doSth execute Started...
doSth call finished.
Main finished.

如果我删除 Thread.Sleep(5000) 并使主线程不休眠,并且在控制台应用程序中,它将丢失输出并且不会调用 doSth 函数的第二个输出。 在 Winforms 应用程序中,没有效果。

首先,async/await 并不意味着会有其他线程在执行。 如果 await 操作符被命中,那么那段代码的执行就会停在那里(并等待),而在这个操作等待的同时,同一个线程可以接受更多的工作(更多的任务要做)。

因此,异步代码是尽可能多地重用相同的线程,而不是跨越尽可能多的线程执行代码。

删除Thread.Sleep您只需完成工作即可。 您刚刚完成运行应用程序,而后台的任务仍在运行。 但它已因您的应用程序而终止。

最终,无论async void您键入async void ,您都可能犯了一个错误(对 winform 上的事件处理程序稍加注意); 这里的解决方法是等待(如果您唯一的非后台线程,即Main入口点,退出:然后控制台应用程序终止):

static async Task Main(string[] args)
{
    await doSthAsync();
    // ...
}

private static async Task doSthAsync() {...}

请注意,异步和并发/并行是非常不同的事情 如果您的意图是实现并发,那么也许:

static async Task Main(string[] args)
{
    var pending = Task.Run(doSthAsync);
    // ...
    await pending; // to allow parallel work to finish
}

其他答案解释了为什么Thread.Sleep使应用程序保持活动状态,直到您可以看到所有输出。

我来回答你的第一个问题:

在 ButtonClicked 事件处理函数中调用此 doSth。 它会在另一个线程中延迟 3000 毫秒,并在 MainThread 中输出“doSth execute finished”。 但是在这个控制台应用程序中,它在工作线程中输出“doSth execute finished”,为什么?

旁注:延迟不是“在另一个线程中”完成的。 这只是一个计时器。

线程不同的原因是 UI 应用程序具有“上下文”。 这个“上下文”await捕获并用于恢复执行async方法(正如我在我的博客中所描述的那样)。 UI 应用程序中的 UI 上下文将继续在主 UI 线程上执行async方法。 相比之下,控制台应用程序没有“上下文”,因此async方法会在线程池线程上恢复执行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM