2010-01-20 66 views
43

这是很难找到工人的详细而简单的描述和I/O线程在.NET工人的简单描述和I/O线程在.NET

什么是很清楚,我就这个话题(但不得技术上精确):

  • 工作线程,线程应该雇用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线程时会更有效地进行上下文切换?

回答

47

.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开发人员不会直接使用它。

'worker thread'和'I/O thread'之间确实没有技术上的区别 - 它们都只是普通的线程。但是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 管理线程池:BindHandle的http://msdn.microsoft.com/en-us/library/0ka9477y.aspx 例如:http://blogs.msdn.com/junfeng/archive/2008/12/01/threadpool-bindhandle.aspx

+1

所以一些API,它暴露APM模式(例如 - 的WebRequest),使用ThreadPool.BindHandle方法在内部。 BeginXXX方法将使用用户回调委托,给予某些设备驱动程序工作并保留ThreadPool I/O线程以“等待”完成端口通知外部设备工作完成。当收到通知时,I/O线程唤醒并运行用户回调委托代码(应该很快,以便将I/O线程快速返回到池)。 – Konstantin 2010-01-31 14:14:32

+1

所以工作线程通常运行工作,按用户代码调度。 I/O线程通常运行工作,由框架代码调度,该框架代码与外部设备交互,需要等待完成工作并运行用户回调逻辑,此时“外部”工作完成。 这一切都正确的理解? – Konstantin 2010-01-31 14:17:48

+2

几乎,但操作系统如何调度实际的I/O对于CLR是不透明的。 CLR的I/O线程不会被束缚等待完成;而是在操作完成时,操作系统通过I/O完成端口提醒CLR。 – alexdej 2010-02-01 18:02:24

2

简单的说一个工作者线程是为了执行短时间的工作,并且在完成工作时会自行删除。可以使用回调来通知父进程它已完成或传回数据。

I/O线程将一直执行相同的操作或一系列操作,直到父进程停止。这就是所谓的,因为它通常是设备驱动程序连续运行监视设备端口。一个I/O线程通常会创建事件,只要它希望与其他线程通信。

所有进程以线程运行。 您的应用程序作为线程运行。 任何线程都可能产生工作线程或I/O线程(如同您称之为)。

性能与所用线程的数量或类型之间总是保持良好平衡。过程处理的回调或事件过多将严重降低其性能,因为处理它们时主循环的中断次数较多。

工作线程的例子是在用户交互后将数据添加到数据库中,或执行长时间的数学计算或将数据写入文件。通过使用工作者线程释放主应用程序,这对GUI非常有用,因为它在执行任务时不会冻结。

0

比我有更多技能的人会跳进来帮忙。

工作线程有很多的状态,他们是由处理器等计划,你控制他们所做的一切。

IO完成端口由操作系统为涉及很少共享状态的特定任务提供,因此使用起来更快。 .Net中的一个很好的例子就是WCF框架。对WCF服务的每个“调用”实际上都是由IO完成端口执行的,因为它们是启动速度最快的操作系统,操作系统会为您服务。

6

我将首先描述NT中程序如何使用异步I/O。

您可能熟悉Win32 API函数ReadFile的(作为一个例子),它是围绕本机API函数NtReadFile然后的包装。此功能允许您做异步I/O两件事情:

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

然而,当I/O操作完成时有第三种通知方式。您可以创建一个I/O完成端口对象并将它与文件句柄关联。每当在与I/O完成端口关联的文件上完成操作时,操作结果(如I/O状态)就会排队到I/O完成端口。然后您可以设置一个专用线程来从队列中移除结果并执行适当的任务,如调用回调函数。这实质上就是“I/O工作者线程”。

正常的“工作者线程”非常相似;不是从队列中移除I/O结果,而是从队列中移除工作项。您可以排队工作项目(QueueUserWorkItem)并让工作线程执行它们。这可以防止您每次想异步执行任务时必须产生一个线程。