【问题标题】:Getting absurd times for thread creation in Windows when using CreateThread()使用 CreateThread() 在 Windows 中创建线程的时间很荒谬
【发布时间】:2020-02-22 12:15:23
【问题描述】:

所以我创建了以下简单程序来测量在我的机器上创建进程或线程的平均时间:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> 
#include <unistd.h> 
#include <time.h>

DWORD WINAPI ThreadFunc(void* data) {

  return 0;
}

int main(int argc, char** argv) {

    STARTUPINFO si = {0};
    PROCESS_INFORMATION pi = {0};
    clock_t begin, end;
    double cpu_time_used;

    for(int i = 0; i <= 1000000;i++){
        begin = clock();
        CreateProcess(NULL, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
        end = clock();
        cpu_time_used += ((double) (end - begin)) / CLOCKS_PER_SEC;
        TerminateProcess(pi.hProcess, 0);
    }
    /* since we're running 1 000 000 (1 million) times we divide by 1 
     * million and since to get the time in ns instead of ns we multiply by
     * 1 billion we simply multiply by 1000 since 1 billion / 1 million = 1000.
     */
    printf("Average time to create a process = %f ns\n", cpu_time_used * 1000); 

    cpu_time_used = 0;

    for(int i = 0; i < 1000000;i++){
        begin = clock();
        CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);
        end = clock();
        cpu_time_used += ((double) (end - begin)) / CLOCKS_PER_SEC;
    }
    /* since we're running 1 000 000 (1 million) times we divide by 1 
     * million and since to get the time in ns instead of ns we multiply by
     * 1 billion we simply multiply by 1000 since 1 billion / 1 million = 1000.
     */
    printf("Average time to create a Thread = %f ns\n", cpu_time_used * 1000);
    return (EXIT_SUCCESS);
}

以下结果:

Average time to create a process = 89.000000 ns
Average time to create a Thread = 112055.000000 ns

创建一个进程的时间似乎很合理,但为什么创建一个线程的时间比创建一个全新的进程要长一千倍呢?

【问题讨论】:

  • 由于您将空的lpApplicationNamelpCommandLine 参数都传递给CreateProcess,因此无需运行任何内容,也无需创建进程。所以它什么也没做。
  • 您的计时计算也存在缺陷...clock 函数不会为您提供 Windows 中经过的 CPU 时间(使用 VS CRT),它会为您提供 墙时钟 计时。所以你计算的时间还包括创建线程运行和主线程休眠的时间。
  • clock() 并没有按照你想象的方式工作。请改用 QueryPerformanceCounter()。
  • “可执行文件中调用 C 运行时库 (CRT) 的线程应使用 _beginthreadex 和 _endthreadex 函数进行线程管理”(来源 docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/…)。完全没有 C 运行时你不可能做任何事情,所以我的选择是安全而不是抱歉并使用它们。

标签: c windows multithreading


【解决方案1】:

好的,我的方法有几个错误,我将在这里解释,最后附上完整的最终代码。

  1. CreateProcess() 调用什么都不做,而不是启动一个什么都不做的程序。 为了解决这个问题,我们将CreateProcess(NULL, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &amp;si, &amp;pi); 更改为CreateProcess("C:\\Windows\\System32\\rundll32.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &amp;si, &amp;pi);,并且我们暂停了这个过程。
  2. Windows 将程序限制为 2048 个线程。这意味着在第 2048 个线程之后发生了一个额外的瓶颈,因为程序等待线程关闭以打开一个新线程。为了避免这种情况,我将循环减少到只有 1000 个。
  3. clock() 对于多线程程序是不准确的,正如@Some 程序员老兄所指出的那样,我们使用 QueryPerformanceCounter()。请记住,根据您的 IDE 和其他因素,QueryPerformanceFrequency 可能会错误地返回 10 MHz(NetBeans 存在此问题,因此更改为 Visual Studio),因此请先进行测试。

固定的最终代码为:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h> 

#include <time.h>

DWORD WINAPI ThreadFunc(void* data) {

    return 0;
}

int main(int argc, char** argv) {

    STARTUPINFO si = { 0 };
    PROCESS_INFORMATION pi = { 0 };

    LARGE_INTEGER begin, end, cpu_time_used;
    LARGE_INTEGER Frequency;
    cpu_time_used.QuadPart = 0;

    QueryPerformanceFrequency(&Frequency);


    for (int i = 0; i <= 1000; i++) {
        QueryPerformanceCounter(&begin);
        CreateProcess("C:\\Windows\\System32\\rundll32.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
        QueryPerformanceCounter(&end);
        cpu_time_used.QuadPart += end.QuadPart - begin.QuadPart;
        TerminateProcess(pi.hProcess, 0);
    }
    cpu_time_used.QuadPart *= 1000000;
    cpu_time_used.QuadPart /= Frequency.QuadPart;

    printf("Average time to create a process = %lld us\n", cpu_time_used.QuadPart / 1000);

    cpu_time_used.QuadPart = 0;
    HANDLE hThread;
    for (int i = 0; i <= 1000; i++) {
        QueryPerformanceCounter(&begin);
        hThread = CreateThread(NULL, 0, ThreadFunc, NULL, CREATE_SUSPENDED, NULL);
        QueryPerformanceCounter(&end);
        cpu_time_used.QuadPart += end.QuadPart - begin.QuadPart;
    }
    cpu_time_used.QuadPart *= 1000000;
    cpu_time_used.QuadPart /= Frequency.QuadPart;

    printf("Average time to create a Thread = %lld us\n", cpu_time_used.QuadPart / 1000);
    return (EXIT_SUCCESS);
}

返回:

Average time to create a process = 8289 us
Average time to create a Thread = 26 us

【讨论】:

  • 那些时间似乎与我几年前在 CO/Vista 上测量的值大致一致。我可以添加:'当现有线程正在等待的信号量发出信号并且有一个内核可用于运行它时,现有线程开始运行的平均时间 = 4 us。
猜你喜欢
  • 1970-01-01
  • 2019-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-07
  • 2016-06-04
  • 2017-12-03
  • 1970-01-01
相关资源
最近更新 更多