当异步方法在线程上使用时间时,这意味着什么?

Vin*_*cio 2 c# asynchronous async-await

来自MSDN:

async和await关键字不会导致创建其他线程.异步方法不需要多线程,因为异步方法不能在自己的线程上运行.该方法在当前同步上下文上运行,并仅在方法处于活动状态时在线程上使用时间.

我从"异步和等待关键字不会导致创建其他线程"中得到的结论是它只是使用已经从线程池创建的线程,这很好,但我理解的是"在线程上使用时间"当方法有效时".

这是否意味着当我等待任务时,任务"移动"到当前线程还是将结果编组回当前线程?如果查看输出,结果将返回到不同的线程ID,但在完成后在原始线程上完成.我想我错过了这两个行动的"介于两者之间".

void Main()
{   
    var tasks = new List<Task<byte[]>>();

    Console.WriteLine ("Starting on Managed Thread ID {0}, Background {1}, Pool {2}\n", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

    for (int i = 0; i < 5; i++)
    {
        tasks.Add(WriteFile(@"D:\Temp\" + i + ".txt", new String('*', i)));
    }

    Console.WriteLine ("Before Wait()ing on Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

    Task.WhenAll(tasks).Wait();

    Console.WriteLine ("Completed on Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);
}

async Task<byte[]> WriteFile(string path, string text)
{
    FileStream fs = new FileStream(path, FileMode.Create);

    try
    {   
        Console.WriteLine ("Writing to file " + path + " . Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

        var bytes = Encoding.Unicode.GetBytes(text);

        await fs.WriteAsync(bytes, 0, bytes.Length);

        Console.WriteLine ("Finished writing to file " + path  + ". Managed Thread ID {0}, Background {1}, Pool {2}", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

        return new byte[0];
    }
    finally
    {
        Console.WriteLine ("Finally! " + path + ". Managed Thread ID {0}, Background {1}, Pool {2}\n", 
            Thread.CurrentThread.ManagedThreadId,
            System.Threading.Thread.CurrentThread.IsBackground,
            System.Threading.Thread.CurrentThread.IsThreadPoolThread);

        fs.Dispose();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

Starting on Managed Thread ID 46, Background True, Pool False

Writing to file D:\Temp\0.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\0.txt. Managed Thread ID 12, Background True, Pool True
Finally! D:\Temp\0.txt. Managed Thread ID 12, Background True, Pool True

Writing to file D:\Temp\1.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\1.txt. Managed Thread ID 12, Background True, Pool True
Finally! D:\Temp\1.txt. Managed Thread ID 12, Background True, Pool True

Writing to file D:\Temp\2.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\2.txt. Managed Thread ID 12, Background True, Pool True
Finally! D:\Temp\2.txt. Managed Thread ID 12, Background True, Pool True

Writing to file D:\Temp\3.txt . Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\3.txt. Managed Thread ID 24, Background True, Pool True
Finally! D:\Temp\3.txt. Managed Thread ID 24, Background True, Pool True

Writing to file D:\Temp\4.txt . Managed Thread ID 46, Background True, Pool False
Before Wait()ing on Managed Thread ID 46, Background True, Pool False
Finished writing to file D:\Temp\4.txt. Managed Thread ID 18, Background True, Pool True
Finally! D:\Temp\4.txt. Managed Thread ID 18, Background True, Pool True

Completed on Managed Thread ID 46, Background True, Pool False
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 7

我从"异步和等待关键字不会导致创建其他线程"中得出的结论是它只使用已经从线程池创建的线程

不,这根本不是真的.async并且await不要在语言级别使用线程池.使用async/await编写程序是完全可能的,它只使用初始的"主"线程.

这并不意味着一切都会发生在一个线程上 - 只是async/await不会强制创建额外的线程.编译器本身不会生成任何代码来执行此操作.这完全取决于你正在等待的东西.例如,您可以使用任务来执行任务Task.Run,该任务将(通常)使用线程池,您可以等待它.或者你可以等待调用的结果Task.Delay,这本身不会启动任何线程,只是安排在未来完成任务.或者您可以使用异步IO,它可能使用IO完成端口,但实际上不会涉及另一个运行任务"活动"的大部分时间的线程.

一个设计良好的awaitable(如果你使用内置的东西,这将包括你)将安排延续(所有的代码后await),作为你在你开始等待,除非你明确已经相同的上下文告诉它不要."上下文"可能意味着您回到同一个线程(例如在UI中)但可能不会(例如在线程池上下文中,延续运行的线程池线程无关紧要).

在我看来,值得进一步了解异步真正做什么.围绕这个有很多资源,免费和付费墙后面: