Windows 多线程强制执行线程优先级
Windows multi-threading enforcing thread priority
我期待发生的事情:
由于进程亲和性仅限于单核,因此当前进程的所有线程都必须竞争执行时间。
由于线程 a
的优先级高于线程 b
并且它从不阻塞,因此线程 b
永远不会被执行。
我所看到的:
线程 b
被误执行。
为什么会这样?有没有办法使用 WinAPI 强制执行此执行逻辑?
#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;
DWORD WINAPI a_function(LPVOID lpParam)
{
while (true) {
}
return 0;
}
DWORD WINAPI b_function(LPVOID lpParam)
{
while (true) {
cout << "b" << endl;
}
return 0;
}
int main()
{
DWORD affinityMask = 1u;
BOOL res = SetProcessAffinityMask(GetCurrentProcess(), affinityMask);
if (!res) {
return -1;
}
HANDLE a_handle = CreateThread(nullptr, 0, a_function, nullptr, CREATE_SUSPENDED, nullptr);
HANDLE b_handle = CreateThread(nullptr, 0, b_function, nullptr, CREATE_SUSPENDED, nullptr);
SetThreadPriority(a_handle, THREAD_PRIORITY_NORMAL);
SetThreadPriority(b_handle, THREAD_PRIORITY_BELOW_NORMAL);
ResumeThread(a_handle);
ResumeThread(b_handle);
WaitForSingleObject(a_handle, INFINITE);
return 0;
}
输出:
b
b
b
b
b
注意:我没有使用 ResumeThread()
/ SuspendThread()
因为:
“SuspendThread 函数不适用于线程同步,因为它不控制代码中线程执行的暂停点。” (source)
优先级不是绝对的。只要您使用多线程,并且所有线程都处于 运行 可用状态(即,它们没有挂起或等待同步原语),它们 将 运行迟早。优先级仅确保具有更高优先级的线程以更少的延迟/更大的时间片进行调度,但不能确保它们在任何其他 class 线程获得机会之前被排他性地调度。
基本原理与您构建的非常相似:自旋锁。如果线程 a
是消费者,而线程 b
是生产者,那么 运行ning b
永远不会将 a
送入死锁。这个问题通常被称为 Priority Inversion,因为 b
在技术上比 a
优先,因为 a
正在等待 b
, 但配置的优先级和不使用内核同步对象并不反映这种依赖性。
这不仅仅是一个假设的场景,而且实际上很常见。您可能会争辩说您不会在多线程系统上注意到,但理由再次是您不能仅仅通过将软件限制为单个 CPU 内核来冒险破坏软件,或者因为所有内核都得到被高优先级线程占用。
我期待发生的事情:
由于进程亲和性仅限于单核,因此当前进程的所有线程都必须竞争执行时间。
由于线程 a
的优先级高于线程 b
并且它从不阻塞,因此线程 b
永远不会被执行。
我所看到的:
线程 b
被误执行。
为什么会这样?有没有办法使用 WinAPI 强制执行此执行逻辑?
#include <iostream>
#include <thread>
#include <windows.h>
using namespace std;
DWORD WINAPI a_function(LPVOID lpParam)
{
while (true) {
}
return 0;
}
DWORD WINAPI b_function(LPVOID lpParam)
{
while (true) {
cout << "b" << endl;
}
return 0;
}
int main()
{
DWORD affinityMask = 1u;
BOOL res = SetProcessAffinityMask(GetCurrentProcess(), affinityMask);
if (!res) {
return -1;
}
HANDLE a_handle = CreateThread(nullptr, 0, a_function, nullptr, CREATE_SUSPENDED, nullptr);
HANDLE b_handle = CreateThread(nullptr, 0, b_function, nullptr, CREATE_SUSPENDED, nullptr);
SetThreadPriority(a_handle, THREAD_PRIORITY_NORMAL);
SetThreadPriority(b_handle, THREAD_PRIORITY_BELOW_NORMAL);
ResumeThread(a_handle);
ResumeThread(b_handle);
WaitForSingleObject(a_handle, INFINITE);
return 0;
}
输出:
b
b
b
b
b
注意:我没有使用 ResumeThread()
/ SuspendThread()
因为:
“SuspendThread 函数不适用于线程同步,因为它不控制代码中线程执行的暂停点。” (source)
优先级不是绝对的。只要您使用多线程,并且所有线程都处于 运行 可用状态(即,它们没有挂起或等待同步原语),它们 将 运行迟早。优先级仅确保具有更高优先级的线程以更少的延迟/更大的时间片进行调度,但不能确保它们在任何其他 class 线程获得机会之前被排他性地调度。
基本原理与您构建的非常相似:自旋锁。如果线程 a
是消费者,而线程 b
是生产者,那么 运行ning b
永远不会将 a
送入死锁。这个问题通常被称为 Priority Inversion,因为 b
在技术上比 a
优先,因为 a
正在等待 b
, 但配置的优先级和不使用内核同步对象并不反映这种依赖性。
这不仅仅是一个假设的场景,而且实际上很常见。您可能会争辩说您不会在多线程系统上注意到,但理由再次是您不能仅仅通过将软件限制为单个 CPU 内核来冒险破坏软件,或者因为所有内核都得到被高优先级线程占用。