首页 > 解决方案 > C ++中多线程的安全性与速度

问题描述

如果我有一个要同时由多个线程更新的数组,那么最好/最快的方法是什么?例如,假设我有以下代码:

std::vector<float> vec;
vec.push_back(0.f);
for(int i = 0; i < 10000; i++) {
    std::thread([&]{ 
        // SAFETY CONSTRUCTS GO HERE
        vec[0] += 1; // OR MAYBE HERE
        // AND HERE? 
    });
}
// wait a little while, i.e. I was too lazy to write out joins
std::cout << vec[0];

如果我希望这是安全的并最终打印值 10000,那么最好/最快的方法是什么?

标签: c++multithreadingthread-safety

解决方案


在您给出的示例中,最好/最安全的方法是不启动线程,而只是v[0]在循环中更新。启动和同步线程的开销可能会超过您通过并行执行某些操作所获得的任何好处。

v是一个非原子对象 ( std::vector<float>),v[0]实际上是一个函数调用。此类对象及其非静态成员函数无法保护自己免受多个线程的并发访问。要从多个线程中使用它们,必须同步v(and ) 的每次直接使用。v[0]

通常,通过同步对由多个线程更新和访问的任何变量(或更一般地,内存)的访问来实现涉及并发执行线程的安全性。

如果使用互斥锁,这通常意味着所有访问共享数据的线程必须首先获取互斥锁,对共享变量进行操作(例如 update v[0]),然后释放互斥锁。如果一个线程没有抓取(或抓取然后释放)互斥锁,那么它所做的所有操作都不能触及共享变量。

如果您希望通过线程来提高性能,则需要在每个线程中完成大量工作,而无需对共享变量进行任何访问。由于可以同时执行部分工作,因此该工作可能会在更少的总运行时间内执行。为了代表性能优势,收益(例如通过同时执行大量操作)需要超过成本(启动线程,同步访问由多个线程访问的任何数据)。

在与您显示的代码类似的任何事情中,这都是极不可能的。

关键是,当线程共享任何数据时,速度和安全性之间总是存在权衡。安全需要同步更新共享变量 - 无一例外。性能增益通常来自不需要同步的事物(即不访问线程之间共享的变量)并且可以并行执行。


推荐阅读