Windows线程调度程序是不公平的?

时间:2013-05-09 03:40:54

标签: c windows winapi cpu scheduler

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

#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);
}

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

Screenshot

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

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

4 个答案:

答案 0 :(得分:1)

一些选项:


如果(并且仅当)您运行的是Windows 7/8 64位(非32位)或Windows Server 2008 R2或更高版本,则可以完全绕过系统调度程序自己照顾好这个,虽然这不是一件小事!

它是described on MSDN here,称为用户模式调度(UMS)

以下MSDN的部分截图 - 这对您有用吗?

enter image description here

此外,在BIOS中禁用超线程可能是一个想法(如果适用),因为关于Windows如何区分逻辑和物理内核存在争议。 See here for a discussion

还有一些可用的功能,例如SetThreadAffinityMask()MSDN here)和SetThreadIdealProcessor()可能会有所帮助,尽管我个人认为这有点不可思议。我经常会损害整体吞吐量,而不是帮助它。

答案 1 :(得分:1)

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

答案 2 :(得分:0)

这是一个黑暗猜测的镜头。

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

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

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

答案 3 :(得分: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。”)