.NET中工作程序和I/O线程的简单描述

Kon*_*tin 60 .net multithreading iocp

在.NET中很难找到工作者和I/O线程的详细但简单的描述

我对这个主题有什么了解(但可能在技术上不精确):

  • 工作线程是应该为其工作使用CPU的线程;
  • I/O线程(也称为"完成端口线程")应该为其工作使用设备驱动程序,并且基本上"什么都不做",只监视非CPU操作的完成.

不清楚的是:

  • 虽然方法ThreadPool.GetAvailableThreads返回两种类型的可用线程数,但似乎没有公共API来为I/O线程安排工作.您只能在.NET中手动创建工作线程?
  • 似乎单个I/O线程可以监视多个I/O操作.这是真的吗?如果是这样,为什么ThreadPool默认有这么多可用的I/O线程?
  • 在某些文本中,我读到了I/O线程执行I/O操作完成后触发的回调.这是真的吗?考虑到这个回调是CPU操作,这不是工作线程的工作吗?
  • 更具体一点 - ASP.NET异步页用户I/O线程吗?将I/O工作切换到单独的线程而不是增加最大工作线程数的性能有什么好处?是因为单个I/O线程确实监视多个操作吗?或者Windows在使用I/O线程时进行更有效的上下文切换?

ale*_*dej 65

.net/CLR中的术语"工作线程"通常仅指代主线程以外的任何线程,它代表生成线程的应用程序执行某些"工作"."工作"可能意味着什么,包括等待一些I/O完成.ThreadPool保留工作线程的缓存,因为创建线程的成本很高.

.net/CLR中的术语"I/O线程"是指ThreadPool保留的线程,用于从"重叠"的win32调用(也称为"完成端口I/O")调度NativeOverlapped回调.CLR维护自己的I/O完成端口,并且可以绑定任何句柄(通过ThreadPool.BindHandle API).示例:http://blogs.msdn.com/junfeng/archive/2008/12/01/threadpool-bindhandle.aspx.许多.net API在内部使用此机制来接收NativeOverlapped回调,尽管典型的.net开发人员不会直接使用它.

"工作线程"和"I/O线程"之间确实没有技术差异 - 它们都只是普通线程.但是CLR ThreadPool保留了每个池的独立池,以避免工作线程的高需求耗尽可用于调度本机I/O回调的所有线程,从而可能导致死锁.(想象一下,使用所有250个工作线程的应用程序,每个线程都在等待一些I/O完成).

开发人员在处理I/O回调时确实需要注意,以确保I/O线程返回到ThreadPool - 也就是说,I/O回调代码应该完成服务回调所需的最少工作量然后将线程的控制权返回给CLR线程池.如果需要更多工作,则应该在工作线程上安排该工作.否则,应用程序有可能"劫持"CLR的保留I/O完成线程池以用作正常工作线程,从而导致上述死锁情况.

进一步阅读的一些很好的参考:win32 I/O完成端口:http://msdn.microsoft.com/en-us/library/aa365198( VS.85).aspx托管的线程池:http://msdn.microsoft.com /en-us/library/0ka9477y.aspx BindHandle的例子:http://blogs.msdn.com/junfeng/archive/2008/12/01/threadpool-bindhandle.aspx

  • 因此,工作线程通常运行由用户代码调度的工作。I/O 线程通常运行由框架代码调度的工作,框架代码与外部设备交互,并且需要等待其工作完成并在“外部”工作完成时运行用户回调逻辑。这都是正确的理解吗? (2认同)
  • 几乎,但操作系统如何安排实际的I/O对CLR是不透明的.CLR的I/O线程没有等待完成; 而是当工作完成时,操作系统通过I/O完成端口警告CLR. (2认同)

wj3*_*j32 9

我将首先介绍NT中程序如何使用异步I/O.

您可能熟悉Win32 API函数ReadFile(作为示例),它是Native API函数NtReadFile的包装器.此函数允许您使用异步I/O执行两项操作:

  • 您可以创建一个事件对象并将其传递给NtReadFile.然后,当读取操作完成时,将发出此事件的信号.
  • 您可以将异步过程调用(APC)函数传递给NtReadFile.本质上,这意味着当读取操作完成时,该函数将排队到启动操作的线程,并在线程执行可警告等待时执行.

但是,当I/O操作完成时,会有第三种通知方式.您可以创建I/O完成端口对象并将文件句柄与其关联.只要对与I/O完成端口关联的文件完成操作,操作结果(如I/O状态)就会排队到I/O完成端口.然后,您可以设置专用线程以从队列中删除结果,并执行适当的任务,如调用回调函数.这基本上就是"I/O工作线程".

正常的"工人线程"非常相似; 它不是从队列中删除I/O结果,而是从队列中删除工作项.您可以对工作项(QueueUserWorkItem)进行排队,并让工作线程执行它们.这可以防止您每次要异步执行任务时都必须生成一个线程.