为什么异步代码在 C# 中的工作线程上运行

jpo*_*pou 3 c# multithreading asynchronous async-await

我在 C# 中使用 async/await,虽然我认为我理解大多数概念,但我无法解释为什么代码行“var rxres = await ...”第一次在我的 UDP 线程上运行,之后数据包的进一步接收在工作线程上运行。据我所知,我没有“屈服”回线程函数,它仍然存在,因此所有对 ReceiveAsync 的调用都应该在我创建的线程上运行。

    static void Main(string[] args)
    {
        var ewh = new EventWaitHandle(false, EventResetMode.ManualReset);

        var udpThread = new Thread(async () =>
        {
            ListenUdpAsync().Wait();
        });
        udpThread.Name = "UDP THREAD";
        udpThread.Start();

        ewh.WaitOne();
    }
    static public async Task ListenUdpAsync()
    {
        var localPort = 5555;
        var localBind = new IPEndPoint(IPAddress.Any, localPort);

        using (var udpc = new UdpClient(localBind))
        {
            while(true)
            {
                var rxres = await udpc.ReceiveAsync();
                Console.WriteLine("Rx From: " + rxres.RemoteEndPoint);
                Console.WriteLine("Rx Data: " + Encoding.ASCII.GetString(rxres.Buffer));
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

Yuv*_*kov 5

我不是“屈服”回到线程功能

该方法产生一次点击await udpc.ReceiveAsync(),这就是 async-await 的工作原理。ListenAsync自身同步阻塞,但由于没有同步上下文发挥作用,延续能够将自身编组到任意线程池线程上。

所以 ReceiveAsync 的所有调用都应该在我创建的线程上运行。

并不真地。通常,当您在控制台应用程序中运行时,它使用 default TaskScheduler,它在内部使用线程池在任意线程池线程上执行延续。一个ReceiveAsync()完成,它的延续需要安排在某个地方,那个地方在线程池上。

附带说明 - 没有理由async在委托上使用修饰符,因为您不是在等待内部的任何东西,而是同步阻塞。