6

有时,当我运行这个简单的程序时

#include <Windows.h>

DWORD WINAPI ThreadStart(LPVOID)
{
    for (;;) { }
    return 0;
}

int _tmain()
{
    SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);

    SYSTEM_INFO si;
    GetSystemInfo(&si);
    for (DWORD i = si.dwNumberOfProcessors * 2; i > 0; i--)
    { CloseHandle(CreateThread(NULL, 0, &ThreadStart, NULL, 0, NULL)); }

    Sleep(INFINITE);
}

我经常观察到不公平的线程调度,例如:

截屏

每次运行都不是不公平的,但是当它时,它会在应用程序的整个生命周期中保持不公平。

为什么会发生这种情况,避免它的正确方法是什么?

4

4 回答 4

1

只要不属于您的程序的线程运行,您就会在多处理器系统上看到这一点。假设有一个系统任务启动运行几秒钟,扫描.NET 程序集或其他东西。它会在多个时间片内将您的一个线程从调度中剔除,而其他内核上的线程继续运行。被淘汰的线永远追不上其他线。

于 2013-05-09T08:55:59.967 回答
1

一些选项:


如果(且仅当)您运行的是 Windows 7/8 64 位(不是 32 位)或 Windows Server 2008 R2 或更高版本,您可以完全绕过系统调度程序并自行处理,尽管它不是微不足道的事业!

在 MSDN here 上进行了描述,称为User-Mode Scheduling (UMS)

下面来自 MSDN 的部分截图 - 这对你有用吗?

在此处输入图像描述

此外,在您的 BIOS 中禁用超线程(如果适用)可能是一个想法,因为对于 Windows 如何区分逻辑内核和物理内核存在一些争议。请参阅此处进行讨论

还有一些可用的功能,例如SetThreadAffinityMask()此处为 MSDN),SetThreadIdealProcessor()这可能会有所帮助,尽管我个人觉得这有点偶然。更常见的情况是我损害了整体吞吐量而不是帮助它。

于 2013-05-09T06:18:18.643 回答
0

这是在黑暗中猜测。

但我会建议超线程可能会稍微扭曲结果。

如果您的笔记本电脑的 bios/cmos 允许像某些计算机那样禁用超线程,那么看到您的代码仅在真实内核上运行的数字会很有趣。

即使 HT 是它偏向线程的原因,那么我仍然不知道它为什么会这样做。

于 2013-05-09T07:05:33.553 回答
0

在我的系统上,SetThreadAffinityMask似乎可以缓解这个问题。仍然存在一些不平衡,显然是由于一个核心的可用时间少于另一个,但并不那么严重。

Windows 似乎不愿意在内核之间移动这些线程;他们很少在第一秒左右改变核心。如果线程在内核之间分布不均,则线程时间反映了这一点。

这是我用来尝试亲和掩码的代码:

#include <Windows.h>
#include <stdio.h>

DWORD WINAPI ThreadStart(LPVOID arg)
{
    DWORD pn1, pn2;

    printf("Thread %u on processor %u\n", GetThreadId(GetCurrentThread()), pn1 = GetCurrentProcessorNumber());

    // The problem comes back if you enable this line
    // SetThreadAffinityMask(GetCurrentThread(), -1);

    for (;;) 
    { 
        for (int i = 0; i < 10000; i++);
        pn2 = GetCurrentProcessorNumber();
        if (pn2 != pn1)
        {
            pn1 = pn2;
            printf("Thread %u on processor %u\n", GetThreadId(GetCurrentThread()), pn1);
        }
    }
    return 0;
}

int main(int argc, char ** argv)
{
    SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);

    SYSTEM_INFO si;
    GetSystemInfo(&si);

    for (DWORD i = 0; i < si.dwNumberOfProcessors; i++)
    { 
        SetThreadAffinityMask(CreateThread(NULL, 0, &ThreadStart, NULL, 0, NULL), 1 << i);
        SetThreadAffinityMask(CreateThread(NULL, 0, &ThreadStart, NULL, 0, NULL), 1 << i);  
    }

    Sleep(INFINITE);
    return 0;
}

这种方法似乎也缓解了这个问题,尽管可能没有那么有效:

#include <Windows.h>
#include <stdio.h>

DWORD WINAPI ThreadStart(LPVOID arg)
{
    for (;;);
    return 0;
}

int main(int argc, char ** argv)
{
    SYSTEM_INFO si;
    GetSystemInfo(&si);

    for (DWORD i = si.dwNumberOfProcessors * 2; i > 0; i--)
    { 
        CreateThread(NULL, 0, &ThreadStart, NULL, 0, NULL);
    }

    for (;;)
    {
        SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);
        Sleep(100);
        SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
    }

    return 0;
}

我怀疑我们正在研究一种基于低优先级线程不需要公平调度的假设而设计的节能措施。(可以说,将线程或进程设置为低优先级表示“我不在乎我得到多少 CPU。”)

于 2013-05-16T06:06:59.313 回答