Windows上线程创建缓慢

pkln4tw6  于 2023-04-22  发布在  Windows
关注(0)|答案(2)|浏览(141)

我使用C++11工具将一个数字运算应用程序升级为多线程程序。它在Mac OS X上运行良好,但在Windows(Visual Studio 2013)上无法从多线程中受益。使用以下玩具程序

#include <iostream>
#include <thread>

void t1(int& k) {
    k += 1;
};

void t2(int& k) {
    k += 1;
};

int main(int argc, const char *argv[])
{
    int a{ 0 };
    int b{ 0 };

    auto start_time = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < 10000; ++i) {
        std::thread thread1{ t1, std::ref(a) };
        std::thread thread2{ t2, std::ref(b) };
        thread1.join();
        thread2.join();
    }
    auto end_time = std::chrono::high_resolution_clock::now();
    auto time_stack = std::chrono::duration_cast<std::chrono::microseconds>(
        end_time - start_time).count();
    std::cout << "Time: " << time_stack / 10000.0 << " micro seconds" <<
        std::endl;

    std::cout << a << " " << b << std::endl;

    return 0;
}

我发现在Mac OS X上启动一个线程需要34微秒,而在Windows上启动一个线程需要340微秒。我在Windows端做错了什么吗?是编译器问题吗?

06odsfpq

06odsfpq1#

不是编译器问题(严格来说也不是操作系统问题)。
众所周知,创建线程是一个昂贵的操作,在Windows下尤其如此(在clone之前的Linux下也是如此)。
此外,创建 * 和加入 * 一个线程必然是缓慢的,并且不能告诉很多关于 * 创建 * 一个线程本身的信息。加入假设线程已经退出,这只能在它被调度运行之后发生。因此,您的度量包括调度引入的延迟。到目前为止,您度量的时间实际上是 * 相当不错 *(它们很容易就长了20倍!)。
不过,线程生成速度是否慢并不重要。
在一个 * 真实的 * 程序中创建20,000个像你的基准测试中那样的线程是一个严重的错误。虽然创建数千(甚至数百万)个线程并不是严格非法的或不允许的,但使用线程的“正确”方法是创建的线程不超过大约CPU核心的数量。一个人也不会总是创建非常短命的线程。
你可能会有一些短暂的线程,你可能会创建一些额外的线程(例如,在I/O上阻塞),但您不会希望创建数百或数千个这样的线程。(超出CPU核心的数量)意味着更多的上下文切换、更多的调度器工作、更多的缓存压力每个线程占用1 MB的地址空间和64 kB的物理内存(由于堆栈保留和提交粒度)。
现在,假设你在程序启动时创建了10个线程,这是否总共需要3毫秒并不重要。无论如何,程序启动需要几百毫秒(至少),没有人会注意到差异。

3duebb1j

3duebb1j2#

Visual C++使用Concurrency Runtime(特定于MS)来实现std.thread功能。当您直接调用任何Concurrency Runtime功能/函数时,它会创建一个默认的运行时对象(不详细说明)。或者,当您调用std.thread函数时,它会执行与ConcRT函数相同的操作。
创建默认运行时(或者说调度器)需要一些时间,因此它看起来需要一些时间。然后执行基准标记代码(例如,上述代码的全部)。

编辑:

相关问题