C# 在TPL下启动阻塞I/O请求的线程如何立即返回?
我想在这个问题的开头说几句话:C# 在TPL下启动阻塞I/O请求的线程如何立即返回?,c#,.net,multithreading,asynchronous,task-parallel-library,C#,.net,Multithreading,Asynchronous,Task Parallel Library,我想在这个问题的开头说几句话: 我熟悉C#中的wait关键字生成的IAsyncStateMachine实现 我的问题不是关于使用async和wait关键字时确保的基本控制流 假设A 在任何线程环境中,无论是在Windows操作系统级别、POSIX系统还是在.NET线程池中,默认的线程行为都是当线程请求I/O绑定操作(例如磁盘读取)时,它向磁盘设备驱动程序发出请求并进入等待状态。当然,我在掩饰细节,因为它们不是我们讨论的重点 重要的是,该线程在被设备驱动程序通知其完成的中断解除阻塞之前,无法执行任
wait
关键字生成的IAsyncStateMachine
实现async
和wait
关键字时确保的基本控制流WebClient.downloaddatataskancy
,如果要遵循它们的代码
通过他们(方法的,而不是自己的)卵圆管进入他们的大脑
肠,你会看到它们最终要么执行
同步下载,如果操作失败,则阻止当前线程
被请求同步执行
(Task.RunSynchronously()
)或者如果异步请求,它们
使用
异步编程模型(APM)Begin
和End
方法
这肯定会导致主线程立即返回,因为
它只是将阻塞I/O工作卸载到线程池线程,从而
将diddlysquat添加到应用程序的可伸缩性中
但在这种情况下,在野兽的体内,工作
被秘密卸载到线程池线程。在API的情况下
但事实并非如此,比如说一个API,看起来像这样:
public async Task<string> GetDataAsync()
{
var tcs = new TaskCompletionSource<string>();
// If GetDataInternalAsync makes the network request
// on the same thread as the calling thread, it will block, right?
// How then do they claim that the thread will return immediately?
// If you look inside the state machine, it just asks the TaskAwaiter
// if it completed the task, and if it hasn't it registers a continuation
// and comes back. But that implies that the awaiter is on another thread
// and that thread is happily sleeping until it gets a kick in the butt
// from a wait handle, right?
// So, the only way would be to delegate the making of the request
// to a thread pool thread, in which case, we have not really improved
// scalability but only improved responsiveness of the main/UI thread
var s = await GetDataInternalAsync();
tcs.SetResult(s); // omitting SetException and
// cancellation for the sake of brevity
return tcs.Task;
}
公共异步任务GetDataAsync()
{
var tcs=new TaskCompletionSource();
//如果GetDataInternalAsync发出网络请求
//在与调用线程相同的线程上,它将阻塞,对吗?
//那么,他们如何声称线程将立即返回?
//如果查看状态机内部,它只会询问TaskWaiter
//如果它完成了任务,如果它没有完成,它将注册一个延续
//但这意味着等待者在另一条线上
//那根线在屁股上踢了一脚之前一直在快乐地睡觉
//从一个等待手柄,对吗?
//因此,唯一的办法是授权提出请求
//对于线程池线程,在这种情况下,我们并没有真正改进
//可扩展性,但只提高了主/UI线程的响应能力
var s=等待GetDataInternalAsync();
tcs.SetResult;//忽略SetException和
//为简洁起见取消
返回tcs.Task;
}
如果我的问题看起来毫无意义,请对我温和一点。几乎所有事物的知识范围都是有限的。我只是在学习任何东西。当您谈论异步I/O操作时,正如Stephen Cleary()在这里指出的那样,事实是没有线程。异步I/O操作在低于线程模型的级别上完成。它通常发生在中断处理程序例程中。因此,没有处理请求的I/O线程 您询问启动阻塞I/O请求的线程如何立即返回。答案是因为一个I/O请求并不是在它的核心,实际上是阻塞的。您可以阻塞线程,这样您就有意识地说在I/O请求完成之前不要做任何其他事情,但阻塞的绝不是I/O,而是线程决定旋转(或者可能产生它的时间片) 线程立即返回,因为不必坐在那里轮询或查询I/O操作。这是真正异步的核心。发出I/O请求,最终ISR会弹出完成消息。是的,这可能会冒泡到线程池中以设置任务完成,但这几乎是在不可察觉的时间内发生的