首页 > 解决方案 > 当一项任务阻塞时,线程池如何使用工作线程管理工作项?

问题描述

我的问题基于这篇文章Windows 线程内部

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

在此处输入图像描述

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

在此处输入图像描述

而CLR的线程池架构是

在此处输入图像描述

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

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

我的问题是,

A .can Worker Thread 1 暂时停止执行这个阻塞任务并切换到执行其本地队列中的另一个任务,然后在新任务完成后再次返回执行长时间运行的任务。如果是这种情况,如何保存长时间运行的任务的上下文状态?例如,我们需要将寄存器的值存储到线程内核对象 1 中,但是当 Worker Thread 1 开始执行另一个任务时,原来的任务的保存在线程内核对象 1 中的状态将丢失

或者

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

标签: c#.netwindowsclr

解决方案


A. Worker Thread 1 是否可以暂时停止执行这个阻塞任务,并切换到执行其本地队列中的另一个任务,等新任务完成后再回来执行长时间运行的任务。如果是这种情况,如何保存长时间运行的任务的上下文状态?例如,我们需要将寄存器的值存储到线程内核对象 1 中,但是当 Worker 线程 1 开始执行另一个任务时,原始任务的保存在线程内核对象 1 中的状态将丢失

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

如果您将代码作为线程 1 上的同步块执行,则无法停止阻塞任务以恢复同一线程上的工作,除非您正在执行一些长时间运行的操作,这些操作频繁且明确地允许其他代码有时间执行在同一个线程上执行(见下文)

在 C# 中的另一种方式是,您可以通过使用类中的某些方法(例如/ )以使用await或显式允许其他代码运行的异步方法执行代码。ThreadThread.YeildThread.Sleep

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

在不丢失长时间运行的操作状态的情况下放弃或暂停这些“时间片”的方式是通过使用在编译时或动态(很少)生成的编译状态机。

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


推荐阅读