代码之家  ›  专栏  ›  技术社区  ›  Konstantin

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

  •  49
  • Konstantin  · 技术社区  · 15 年前

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

    我对这个话题有什么明确的看法(但在技术上可能不太准确):

    • 工作线程是指 应该 使用CPU进行工作;
    • I/O线程(也称为“完成端口线程”) 应该 使用设备驱动程序进行工作,基本上“什么都不做”,只监视非CPU操作的完成情况。

    不清楚的是:

    • 尽管方法threadpool.getavailablethreads返回两种类型的可用线程数,但似乎没有公共API来安排I/O线程的工作。您只能在.NET中手动创建工作线程?
    • 似乎单个I/O线程可以监视多个I/O操作。是真的吗?如果是这样,为什么线程池在默认情况下有这么多可用的I/O线程?
    • 在一些文本中,我读到在I/O操作完成后触发的回调是由I/O线程执行的。是真的吗?考虑到这个回调是CPU操作,这不是工作线程的作业吗?
    • 更具体地说,“ASP.NET异步页用户I/O线程吗?”将I/O工作切换到独立线程而不是增加最大工作线程数时,性能究竟有什么好处?是因为单个I/O线程监视多个操作吗?或者Windows在使用I/O线程时更有效地进行上下文切换?
    4 回复  |  直到 7 年前
        1
  •  55
  •   alexdej    15 年前

    .net/clr中的“worker thread”一词通常只指除主线程以外的任何线程,该线程代表生成线程的应用程序执行某些“工作”。“工作”实际上意味着任何事情,包括等待一些I/O完成。线程池保存工作线程的缓存,因为线程的创建成本很高。

    .net/clr中的术语“I/O线程”是指threadpool为从“重叠”win32调用(也称为“完成端口I/O”)调度本机重叠回调而保留的线程。clr维护自己的I/O完成端口,并可以将任何句柄绑定到它(通过threadpool.bindHandle API)。例子如下: http://blogs.msdn.com/junfeng/archive/2008/12/01/threadpool-bindhandle.aspx . 许多.NET API在内部使用此机制来接收本机重叠回调,但典型的.NET开发人员永远不会直接使用它。

    “worker-thread”和“i/o-thread”实际上没有技术上的区别——它们都只是普通线程。但是,clr threadpool将每个线程保持单独的池,以避免工作线程的高需求耗尽了所有可用于调度本机I/O回调的线程,从而可能导致死锁。(假设一个应用程序使用所有250个工作线程,其中每个线程都在等待一些I/O完成)。

    开发人员在处理I/O回调时需要注意一些,以确保I/O线程返回到线程池——也就是说,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

        2
  •  6
  •   wj32    15 年前

    我将从描述NT中的程序如何使用异步I/O开始。

    您可能熟悉win32 api函数 读取文件 (作为示例),它是本机API函数的包装器 ntRADDFILE . 此函数允许您使用异步I/O执行两项操作:

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

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

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

        3
  •  3
  •   ChrisBD    15 年前

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

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

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

    在性能和使用的线程数量或类型之间总是有一个很好的平衡。一个进程处理过多的回调或事件将严重降低其性能,这是由于它处理主进程循环时中断的次数。

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

        4
  •  0
  •   Spence    15 年前

    一个技能比我强的人会跳进来帮忙。

    工作线程有很多状态,它们是由处理器等调度的,您可以控制它们所做的一切。

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