mar*_*v12 6 c# asynchronous async-await
C# 中的许多内置 IO 函数都是非阻塞的,也就是说,它们在等待操作完成时不会保留线程。
例如System.IO.File.ReadAllLinesAsync返回 aTask<string[]>是非阻塞的。
它不只是暂停正在使用的线程,实际上还释放了该线程,以便其他进程可以使用它。
我认为这是通过调用操作系统来实现的,操作系统在检索文件时回调程序,而程序不必浪费线程等待它。
是否可以自己创建一个非阻塞异步任务?
显然,做类似的事情Thread.sleep()不会像那样释放当前线程System.IO.File.ReadAllLinesAsync。
我意识到休眠线程不会占用 CPU 资源,但它仍然占用一个线程,这在处理大量请求的 Web 服务器中可能会出现问题。
我不是在谈论一般如何生成任务。我正在谈论用于处理文件/网络调用的内置 C# 函数如何在等待时释放线程。
评论中似乎对此有相当多的讨论,但我不确定他们中是否有人按照你想要的方式回答它,所以我会尽力而为。
目前,我通常可以通过两种方式来调用没有任务的异步方法。这些通常是旧的 API(例如SqlCommand.BeginExecuteNonQuery),已被基于任务的调用替换。如果您有更具体的场景,那么提供更好的示例将很有帮助。
我正在谈论用于处理文件/网络调用的内置 C# 函数如何在等待时释放线程。
您问这个,但您已经说过“我认为这是通过以操作系统回调程序的方式调用操作系统来完成的”。你有点回答了你自己的问题。这些内置操作正在进行的调用会移交给操作系统,并在完成后收到操作系统的警报。
在我的示例中,假设CallFoo正在调用某种处理所有事务的操作系统操作。操作系统调用方式的实际实现对于您来说并不是非常重要,您无需担心,但如果您想了解更多信息,可以研究从 C# 调用 Windows 内核。
想象一下这个函数看起来像这样:
public void CallFoo(Action finishedCallback);
Run Code Online (Sandbox Code Playgroud)
你希望能够做到这一点,这样你就可以这样称呼它:
public Task CallFoo();
Run Code Online (Sandbox Code Playgroud)
我会这样定义它:
public Task CallFoo()
{
var taskCompletionSource = new TaskCompletionSource();
// Calls to the API that has a non blocking IO call but no async Task API
CallFoo(() =>
{
// Callback is called when the IO task has finished.
// SetResult will mark the returned Task as complete
taskCompletionSource.SetResult();
});
return taskCompletionSource.Task;
}
Run Code Online (Sandbox Code Playgroud)
我认为它工作的另一种方式是使用某种返回的“句柄”,指定异步任务是否已完成。
该方法可能看起来像这样:
public IAsyncHandle CallFoo();
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我会像这样实现它:
public async Task CallFoo()
{
var handle = CallFoo();
while (!handle.IsCompleted)
{
await Task.Delay(100);
}
}
Run Code Online (Sandbox Code Playgroud)
这不太理想,因为您只是轮询以查看它是否完成,但它确实比执行 thread.sleep 使用的资源少得多。明显的缺点是它并没有真正对异步操作完成做出实时反应。您可以根据需要减少/增加延迟。