【问题标题】:How does thread pool manage work item with worker thread when one task is blocking?当一项任务阻塞时,线程池如何使用工作线程管理工作项?
【发布时间】:2021-08-08 15:56:18
【问题描述】:

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

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

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

而CLR的线程池架构是

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

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

我的问题是,

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

或

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

【问题讨论】:

  • 使用异步/等待。

标签: c# .net windows clr


【解决方案1】:

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

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-03-30
    • 1970-01-01
    • 1970-01-01
    • 2012-08-12
    • 1970-01-01
    • 2020-06-12
    • 1970-01-01
    • 2018-01-20
    相关资源
    最近更新 更多