传递对线程的引用时,std :: ref是否真的必要?

时间:2015-03-25 15:13:20

标签: c++ multithreading c++11 stdthread

我通过C ++ Concurrency in Action阅读并在第2章中我开始相信即使是函数原型,例如:

void MagicFunc(Data& myData);

旨在使用如下:

Data dataExample;
thread t(MagicFunc,dataExample);

我应该这样做

Data dataExample
thread t(MagicFunc,std::ref(dataExample));

或其他我希望发生的变化" dataExample"没有成功。具体来说,它表示如下:

  

虽然MagicFunc希望传递第二个参数   引用,std :: thread构造函数t不知道;它的   不知道函数所期望的参数的类型和   盲目地复制提供的值。当它调用Magicfunc时,它会   最终传递对数据的内部副本的引用,而不是   引用数据本身。因此,当线程完成时,   这些更新将作为提供的内部副本丢弃   参数被销毁,process_widget_data将被传递   数据myData未更改,而不是正确更新的版本。

但是,使用以下程序对此进行测试

#include <iostream>
#include <thread>
#include <vector>
#include <chrono>
#include <assert.h>
using namespace std;
using namespace std::chrono;

const int NUM_VALS = 50000000;

#define _MULTICORE 

void AddValuesToSlots(vector<int>& vecVals,vector<int>::iterator& begin,
                      int num,int startNum){
    int i = startNum;
    auto end = begin + num;
    for (auto itr = begin; itr < end; ++itr){
        *itr = i++;
    }
}

int main()
{
    vector<int> vecVals;
    vecVals.resize(NUM_VALS);

    //get number of cores and divide up the workload
    unsigned int numCores = thread::hardware_concurrency();
    unsigned int slotsPerThread = NUM_VALS / numCores;

    //for timing
    high_resolution_clock::time_point t1 = high_resolution_clock::now();


    thread* t = new thread[numCores];

    //get the iterator to the beginning
    auto begin = vecVals.begin();

#ifdef _MULTICORE
    for (int core = 0; core < numCores; ++core){
        t[core] = thread(AddValuesToSlots, vecVals, begin + core*slotsPerThread,
            slotsPerThread, core*slotsPerThread);
    }

    for (int core = 0; core < numCores; ++core){
        t[core].join();
    }
#else
    AddValuesToSlots(vecVals, begin, NUM_VALS, 0);
#endif


    delete[] t;

    //how long did it take?
    high_resolution_clock::time_point t2 = high_resolution_clock::now();
    cout << duration_cast<milliseconds>(t2-t1).count() << endl;

#ifdef _DEBUG
    //test that the values are correct
    for (int slot = 0; slot < NUM_VALS; ++slot)
        assert(vecVals[slot] == slot);
#endif

    return 0;
}

我已尝试将vecVals包含在std::ref中,但两次都没有问题。那么std::ref是否真的有必要并且提供的信息是错误的?

由于

2 个答案:

答案 0 :(得分:4)

你不是直接改变vecVals。迭代器正在工作,因为复制迭代器是好的,它仍然指向相同的内存地址

答案 1 :(得分:4)

根据标准,您发布的代码实际上是非法的。 std::thread应该使用参数的右值副本调用AddValuesToSlots

有些C ++编译器错了,而是用你的参数的左值副本调用它。

live example

测试编译器是否违反规则的简便方法是:

void func1(int&&) { std::cout << "func1\n"; }
void func2(int&) { std::cout << "func1\n"; }

int main() {
  int x;
  std::thread t1(func1, x);
  t1.join();
  std::thread t2(func2, x);
  t2.join();
}

如果t1 ctor被接受且t2被拒绝,则您的编译器符合要求。

如果t2 ctor被接受且t1被拒绝,则您的编译器违反了标准。

See here for more about this MSVC compiler bug