简体   繁体   English

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

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

I want to test async and await in a C# console application, so I write this test code.我想在 C# 控制台应用程序中测试 async 和 await,所以我编写了这个测试代码。 when I use async/await in a Winforms application, for example: call this doSth in a ButtonClicked event handler function.当我在 Winforms 应用程序中使用 async/await 时,例如:在 ButtonClicked 事件处理函数中调用这个 doSth。 it will do delay 3000ms in another thread, and output "doSth execute finished" in MainThread.它会在另一个线程中延迟 3000 毫秒,并在 MainThread 中输出“doSth execute finished”。 but in this console application, it output "doSth execute finished" in the working thread, why?但是在这个控制台应用程序中,它在工作线程中输出“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.");
        }
    }
}

this is the test code in Winforms application这是 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.");
        }
    }
}

这是控制台应用程序中的堆栈跟踪 this is the stack trace in console application, the finally output is :这是控制台应用程序中的堆栈跟踪,最终输出是:

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

这是 Winforms 应用程序中的堆栈跟踪 this is the stack trace in Winforms application, the output is :这是 Winforms 应用程序中的堆栈跟踪,输出为:

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

if I delete Thread.Sleep(5000) and make the main thread don't sleep, and in console application, it will lose the output and the second output of doSth function will not be called.如果我删除 Thread.Sleep(5000) 并使主线程不休眠,并且在控制台应用程序中,它将丢失输出并且不会调用 doSth 函数的第二个输出。 in Winforms application, there is no effect.在 Winforms 应用程序中,没有效果。

First of all, async/await does not mean that there will be other threads executing.首先,async/await 并不意味着会有其他线程在执行。 If await operator is hit, then the execution of that piece of code stops there (and awaits), while this operation waits, the same thread can pick up some more work (some more tasks to do).如果 await 操作符被命中,那么那段代码的执行就会停在那里(并等待),而在这个操作等待的同时,同一个线程可以接受更多的工作(更多的任务要做)。

So asynchronous code is rather about reusing the same threads as much as possible and not about spanning execution of code across as many threads as possible.因此,异步代码是尽可能多地重用相同的线程,而不是跨越尽可能多的线程执行代码。

When deleting Thread.Sleep you simply leave work undone.删除Thread.Sleep您只需完成工作即可。 You just finish running application while there task in the background still running.您刚刚完成运行应用程序,而后台的任务仍在运行。 But it's terminated with your application.但它已因您的应用程序而终止。

Ultimately, any time you type async void , you're probably making a mistake (with a slight caveat around event-handlers on winforms);最终,无论async void您键入async void ,您都可能犯了一个错误(对 winform 上的事件处理程序稍加注意); the fix here is to await things (if your only non-background thread, ie the Main entry-point, exits: then a console application terminates):这里的解决方法是等待(如果您唯一的非后台线程,即Main入口点,退出:然后控制台应用程序终止):

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

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

Note that async and concurrency/parallelism are very different things ;请注意,异步和并发/并行是非常不同的事情 if your intentions were to achieve concurrency, then maybe:如果您的意图是实现并发,那么也许:

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

Other answers explain why the Thread.Sleep keeps the application alive until you can see all the output.其他答案解释了为什么Thread.Sleep使应用程序保持活动状态,直到您可以看到所有输出。

I'll answer your first question:我来回答你的第一个问题:

call this doSth in a ButtonClicked event handler function.在 ButtonClicked 事件处理函数中调用此 doSth。 it will do delay 3000ms in another thread, and output "doSth execute finished" in MainThread.它会在另一个线程中延迟 3000 毫秒,并在 MainThread 中输出“doSth execute finished”。 but in this console application, it output "doSth execute finished" in the working thread, why?但是在这个控制台应用程序中,它在工作线程中输出“doSth execute finished”,为什么?

Side note: the delay is not done "in another thread".旁注:延迟不是“在另一个线程中”完成的。 It's just a timer.这只是一个计时器。

The reason the thread is different is because UI apps have a "context".线程不同的原因是 UI 应用程序具有“上下文”。 This "context" is captured by the await and used to resume executing the async method (as I describe on my blog).这个“上下文”await捕获并用于恢复执行async方法(正如我在我的博客中所描述的那样)。 The UI context in UI applications will resume executing async methods on the main UI thread. UI 应用程序中的 UI 上下文将继续在主 UI 线程上执行async方法。 In contrast, console applications do not have a "context", so async methods resume executing on a thread pool thread.相比之下,控制台应用程序没有“上下文”,因此async方法会在线程池线程上恢复执行。

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

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