当一个任务阻塞时,线程池如何使用工作线程管理工作项?

How does thread pool manage work item with worker thread when one task is blocking?

我的问题是基于这篇文章Internals of Windows Thread

我们可以看到,对于系统中的每一个线程,操作系统都会创建一个线程内核对象。操作系统使用这些线程内核对象来管理和执行系统中的线程

操作系统线程调度器每隔 20 毫秒左右查看一次就绪队列(双向链表)中当前的所有线程内核对象。线程调度程序选择一个线程内核对象并使用最后保存在线程上下文中的值加载 CPU 的寄存器。

而CLR的线程池架构是

每个工作线程都有自己的本地队列,当工作线程调度任务时,任务被添加到调用线程的本地队列中。

在此背景下,假设 .NET ThreadPool 有一些工作线程,如上图所示。假设工作线程 1 在本地队列 1 中有一些工作项目在排队。工作线程 1 正在执行的当前任务阻塞了几秒钟(等待信号到达等,或者它正在执行一个长 运行任务)。

我的问题是,

A.可以让Worker Thread 1暂时停止执行这个阻塞任务,切换到执行本地队列中的另一个任务,等新任务执行完再回来执行长 运行 任务又来了。如果是这样,long运行任务的上下文状态如何保存?比如我们需要将寄存器的值存入Thread Kernel Object 1,但是当Worker Thread 1开始执行另一个任务时,那么保存在线程内核对象 1 中的原始任务状态将丢失

B。工作线程 1 必须先完成这个长 运行 任务,然后它才能执行其本地队列中的其他任务。但这是低效的,因为工作线程 1 浪费了几秒钟,它本可以用来执行其他任务。

A. Can Worker Thread 1 temporarily stop executing this blocking task and switch to execute another task in its local queue, then after the new task finishes and then come back to execute the long running task again. If this is the case, how can the long running task's context state be saved?for example, we need to store the registers' values into the Thread Kernel Object 1, but when Worker Thread 1 starts to execute another task, then the original task's state saved in the Thread Kernel Object 1 will get lost

这实际上取决于您如何执行线程 1 的代码。

如果您在线程 1 上将代码作为同步块执行,则无法停止阻塞任务以恢复同一线程上的工作,除非您正在执行一些长时间的 运行ning 操作经常 显式 允许其他代码在同一线程上执行(见下文)

C# 中的另一种方式,您可以在使用 await 的异步方法中执行代码,或者通过使用 Thread class such as Thread.Yeild / Thread.Sleep 中的某些方法显式允许其他代码 运行。

当您使用这些方法或 await 运算符时,线程会将其剩余时间片放弃给任何准备好 运行 的同等优先级线程。如果没有其他具有同等优先级的线程准备好 运行,则不会暂停当前线程的执行(.Sleep() 除外,它在给定的时间范围内根本没有安排)。

在不丢失长 运行ning 操作状态的情况下放弃或暂停这些 'time slices' 的方式是通过使用编译时生成的已编译 state machines 或即时(很少)。

这些状态机将可能表现为不可中断的代码块的书面代码分解为原始 IL,并将其分解,以便它可以离散执行阻塞并在调度程序分配给 运行 的时间之间保持状态。