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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2099947/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 13:48:55  来源:igfitidea点击:

Simple description of worker and I/O threads in .NET

.netmultithreadingiocp

提问by Konstantin

It's very hard to find detailed but simple description of worker and I/O threads in .NET

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

What's clear to me regarding this topic (but may not be technically precise):

关于这个话题我很清楚(但在技术上可能不准确):

  • Worker threads are threads that shouldemploy CPU for their work;
  • I/O threads (also called "completion port threads") shouldemploy device drivers for their work and essentially "do nothing", only monitor the completion of non-CPU operations.
  • 工作线程是应该使用 CPU 来工作的线程;
  • I/O 线程(也称为“完成端口线程”)应该为它们的工作使用设备驱动程序并且本质上“什么都不做”,只监视非 CPU 操作的完成。

What is not clear:

不清楚的地方:

  • Although method ThreadPool.GetAvailableThreads returns number of available threads of both types, it seems there is no public API to schedule work for I/O thread. You can only manually create worker thread in .NET?
  • It seems that single I/O thread can monitor multiple I/O operations. Is it true? If so, why ThreadPool has so many available I/O threads by default?
  • In some texts I read that callback, triggered after I/O operation completion is performed by I/O thread. Is it true? Isn't this a job for worker thread, considering that this callback is CPU operation?
  • To be more specific – do ASP.NET asynchronous pages user I/O threads? What exactly is performance benefit in switching I/O work to separate thread instead of increasing maximum number of worker threads? Is it because single I/O thread does monitor multiple operations? Or Windows does more efficient context switching when using I/O threads?
  • 尽管方法 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 线程时进行更有效的上下文切换?

回答by alexdej

The term 'worker thread' in .net/CLR typically just refers to any thread other than the Main thread that does some 'work' on behalf of the application that spawned the thread. 'Work' could really mean anything, including waiting for some I/O to complete. The ThreadPool keeps a cache of worker threads because threads are expensive to create.

.net/CLR 中的术语“工作线程”通常仅指除主线程之外的任何线程,它们代表生成线程的应用程序执行某些“工作”。“工作”实际上可能意味着任何事情,包括等待某些 I/O 完成。ThreadPool 保留了工作线程的缓存,因为创建线程的成本很高。

The term 'I/O thread' in .net/CLR refers to the threads the ThreadPool reserves in order to dispatch NativeOverlapped callbacks from "overlapped" win32 calls (also known as "completion port I/O"). The CLR maintains its own I/O completion port, and can bind any handle to it (via the ThreadPool.BindHandle API). Example here: http://blogs.msdn.com/junfeng/archive/2008/12/01/threadpool-bindhandle.aspx. Many .net APIs use this mechanism internally to receive NativeOverlapped callbacks, though the typical .net developer won't ever use it directly.

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

There is really no technical difference between 'worker thread' and 'I/O thread' -- they are both just normal threads. But the CLR ThreadPool keeps separate pools of each simply to avoid a situation where high demand on worker threads exhausts all the threads available to dispatch native I/O callbacks, potentially leading to deadlock. (Imagine an application using all 250 worker threads, where each one is waiting for some I/O to complete).

“工作线程”和“I/O 线程”之间确实没有技术区别——它们都只是普通线程。但是 CLR ThreadPool 保留每个池的单独池只是为了避免对工作线程的高需求耗尽所有可用于调度本机 I/O 回调的线程的情况,这可能导致死锁。(想象一个使用所有 250 个工作线程的应用程序,其中每个线程都在等待一些 I/O 完成)。

The developer does need to take some care when handling an I/O callback in order to ensure that the I/O thread is returned to the ThreadPool -- that is, I/O callback code should do the minimum work required to service the callback and then return control of the thread to the CLR threadpool. If more work is required, that work should be scheduled on a worker thread. Otherwise, the application risks 'hiHymaning' the CLR's pool of reserved I/O completion threads for use as normal worker threads, leading to the deadlock situation described above.

开发人员在处理 I/O 回调时确实需要小心,以确保 I/O 线程返回到 ThreadPool —— 也就是说,I/O 回调代码应该完成为回调提供服务所需的最少工作然后将线程的控制权返回给 CLR 线程池。如果需要更多工作,则应在工作线程上安排该工作。否则,应用程序可能会“劫持”CLR 的保留 I/O 完成线程池以用作普通工作线程,从而导致上述死锁情况。

Some good references for further reading: win32 I/O completion ports: http://msdn.microsoft.com/en-us/library/aa365198(VS.85).aspxmanaged threadpool: http://msdn.microsoft.com/en-us/library/0ka9477y.aspxexample of BindHandle: http://blogs.msdn.com/junfeng/archive/2008/12/01/threadpool-bindhandle.aspx

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

回答by wj32

I'll begin with a description of how asynchronous I/O is used by programs in NT.

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

You may be familiar with the Win32 API function ReadFile(as an example), which is a wrapper around the Native API function NtReadFile. This function allows you to do two things with asynchronous I/O:

您可能熟悉 Win32 API 函数ReadFile(作为示例),它是本机 API 函数NtReadFile的包装器。这个函数允许你用异步 I/O 做两件事:

  • You can create an event object and pass it to NtReadFile. This event will then be signaled when the read operation completes.
  • You can pass an asynchronous procedure call (APC) function to NtReadFile. Essentially what this means is that when the read operation completes, the function will be queued to the thread which initiated the operation and it will be executed when the thread performs an alertable wait.
  • 您可以创建一个事件对象并将其传递给NtReadFile。当读取操作完成时,此事件将被发出信号。
  • 您可以将异步过程调用 (APC) 函数传递给NtReadFile。本质上,这意味着当读取操作完成时,该函数将排队到启动该操作的线程,并在线程执行alertable wait时执行。

There is however a third way of being notified when an I/O operation completes. You can create an I/O completion portobject and associate file handles with it. Whenever an operation is completed on a file which is associated with the I/O completion port, the results of the operation (like I/O status) is queued to the I/O completion port. You can then set up a dedicated thread to remove results from the queue and perform the appropriate tasks like calling callback functions. This is essentially what an "I/O worker thread" is.

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

A normal "worker thread" is very similar; instead of removing I/O results from a queue, it removes work items from a queue. You can queue work items (QueueUserWorkItem) and have the worker threads execute them. This prevents you from having to spawn a thread every single time you want to perform a task asynchronously.

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

回答by ChrisBD

Simply put a worker thread is meant to perform a short period of work and will delete itself when it has completed it. A callback may be used to notify the parent process that it has completed or to pass back data.

简单地说,工作线程旨在执行一小段工作,并在完成后将自身删除。回调可用于通知父进程它已完成或传回数据。

An I/O thread will perform the same operation or series of operations continuously until stopped by the parent process. It is so called because it typically device drivers run continuously monitor the device port. An I/O thread will typically create Events whenever it wishes to communicate to other threads.

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

All processes run as threads. Your application runs as a thread. Any thread may spawn worker threads or I/O threads (as you call them).

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

There is always a fine balance between performance and the number or type of threads used. Too many callbacks or Events handled by a process will severely degrade its performance due to the number of interruptions to its main process loop as it handles them.

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

Examples of a worker thread would be to add data into a database after user interaction or to perform a long mathematical calculation or write data to a file. By using a worker thread you free up the main application, this is most useful for GUIs as it doesn't freeze whilst the task is being performed.

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

回答by Spence

Someone with more skills than me is going to jump in here to help out.

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

Worker threads have a lot of state, they are scheduled by the processor etc. and you control everything they do.

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

IO Completion Ports are provided by the operating system for very specific tasks involving little shared state, and thus are faster to use. A good example in .Net is the WCF framework. Every "call" to a WCF service is actually executed by an IO Completion Port because they are the fastest to launch and the OS looks after them for you.

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