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

时间:2010-01-20 08:29:06

标签: .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线程时进行更有效的上下文切换?

4 个答案:

答案 0 :(得分:59)

.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

答案 1 :(得分:7)

我将首先介绍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 )进行排队,并让工作线程执行它们。这可以防止您每次要异步执行任务时都必须生成一个线程。

答案 2 :(得分:3)

简单地说,一个工作线程意味着执行一段短时间的工作,并在完成它时自行删除。回调可用于通知父进程它已完成或传回数据。

I / O线程将持续执行相同的操作或一系列操作,直到父进程停止。之所以这么称呼,是因为通常设备驱动程序会持续监视设备端口。 I / O线程通常会在希望与其他线程通信时创建事件。

所有进程都以线程运行。 您的应用程序作为线程运行。 任何线程都可能产生工作线程或I / O线程(就像你调用它们一样)。

性能与使用的线程数量或类型之间始终存在良好的平衡。进程处理的回调或事件过多会严重降低其性能,因为它处理主进程循环时会有多次中断。

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

答案 3 :(得分:0)

比我更有技巧的人会跳到这里帮忙。

工作线程有很多状态,它们由处理器等安排,你可以控制它们所做的一切。

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