在Windows下创建和终止线程需要多长时间?

Sma*_*acL 20 c++ performance multithreading

我已将一个复杂的数组处理任务拆分为多个线程,以利用多核处理,并看到了很多好处.目前,在任务开始时我创建线程,然后在完成工作时等待它们终止.我通常创建的线程数量是核心数量的四倍,因为每个线程可能需要花费不同的时间,并且拥有额外的线程可确保所有核心在大多数时间内保持占用状态.我想知道在程序启动时创建线程会有多大的性能优势,让它们保持空闲直到需要,并在我开始处理时使用它们.更简单地说,在线程内部处理之上和之后开始和结束新线程需要多长时间?我现在正在使用线程

CWinThread *pMyThread = AfxBeginThread(CMyThreadFunc,&MyData,THREAD_PRIORITY_NORMAL);
Run Code Online (Sandbox Code Playgroud)

通常,我将在64位架构上使用8个内核的32个线程.该过程目前需要<1秒,并且每次刷新显示时都会启动.如果开始和结束一个线程<1ms,则返回不能证明这一点.我在分析这个问题时遇到了一些困难.

这里相关问题有所帮助,但对于我所追求的内容有点模糊.任何反馈意见.

Jer*_*fin 17

我不久前写过这篇文章的时候,我有同样的基本问题(以及另一个显而易见的问题).我更新了它,不仅展示了创建线程所需的时间,还展示了线程开始执行所需的时间:

#include <windows.h>
#include <iostream>
#include <time.h>
#include <vector>

const int num_threads = 32;

const int switches_per_thread = 100000;

DWORD __stdcall ThreadProc(void *start) {
    QueryPerformanceCounter((LARGE_INTEGER *) start);
    for (int i=0;i<switches_per_thread; i++)
        Sleep(0);
    return 0;
}

int main(void) {
    HANDLE threads[num_threads];
    DWORD junk;

    std::vector<LARGE_INTEGER> start_times(num_threads);

    LARGE_INTEGER l;
    QueryPerformanceCounter(&l);

    clock_t create_start = clock();
    for (int i=0;i<num_threads; i++)
        threads[i] = CreateThread(NULL, 
                            0, 
                            ThreadProc, 
                            (void *)&start_times[i], 
                            0, 
                            &junk);
    clock_t create_end = clock();

    clock_t wait_start = clock();
    WaitForMultipleObjects(num_threads, threads, TRUE, INFINITE);
    clock_t wait_end = clock();

    double create_millis = 1000.0 * (create_end - create_start) / CLOCKS_PER_SEC / num_threads;
    std::cout << "Milliseconds to create thread: " << create_millis << "\n";
    double wait_clocks = (wait_end - wait_start);
    double switches = switches_per_thread*num_threads;
    double us_per_switch = wait_clocks/CLOCKS_PER_SEC*1000000/switches;
    std::cout << "Microseconds per thread switch: " << us_per_switch;

    LARGE_INTEGER f;
    QueryPerformanceFrequency(&f);

    for (auto s : start_times) 
        std::cout << 1000.0 * (s.QuadPart - l.QuadPart) / f.QuadPart <<" ms\n";

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

样品结果:

Milliseconds to create thread: 0.015625
Microseconds per thread switch: 0.0479687
Run Code Online (Sandbox Code Playgroud)

前几个线程启动时间如下所示:

0.0632517 ms
0.117348 ms
0.143703 ms
0.18282 ms
0.209174 ms
0.232478 ms
0.263826 ms
0.315149 ms
0.324026 ms
0.331516 ms
0.3956 ms
0.408639 ms
0.4214 ms
Run Code Online (Sandbox Code Playgroud)

请注意,虽然这些都是单调递增的,但这并不能保证(尽管这个方向肯定存在趋势).

当我第一次写这篇文章时,我使用的单位更有意义 - 在33 MHz 486上,这些结果并非像这样的小分数.:-)我想有一天,当我有野心勃勃的时候,我应该重写这个std::async用来创建线程并std::chrono做时机,但......

  • @DanielHsH,你有没有参考删除Sleep(0)的编译器,因为它对我来说似乎是一个不寻常的优化.假设Sleep(0)放弃了线程的剩余时间片,删除它显然会影响多线程程序的性能. (3认同)
  • Jerry Coffin,你的代码非常错误.您没有正确测量时间.线程的创建是异步操作.当您测量'create_end'时,它会测量请求64个线程而不是实际创建线程所花费的时间.切换时间测量也是如此.你的代码是完全错误的,它只会让别人感到困惑.请修理或删除它. (2认同)
  • 杰瑞.当你调用'CreateThread'时 - 这是一个异步方法.即使在创建第一个线程之前,创建64个线程的循环也可以终止(64次调用Create线程).我会举个例子.获得美国咧嘴卡需要多长时间?可能只有几个月或几年.但是,只需几分钟就可以向美国移民局发送信件,要求提供咧嘴笑容.发信后,给你咧嘴笑的法律辩论是异步的.您的代码衡量发送信件所需的时间. (2认同)