std::ref 在传递对线程的引用时真的有必要吗?
Is std::ref really necessary when passing references to threads?
我正在通读 C++ 并发实战,在第 2 章中我被引导相信即使是函数原型,例如:
void MagicFunc(Data& myData);
旨在像下面这样使用:
Data dataExample;
thread t(MagicFunc,dataExample);
我真的应该这样做
Data dataExample
thread t(MagicFunc,std::ref(dataExample));
否则,我预计发生在 "dataExample" 上的变化将不会发生。具体来说,它是这样的:
Although MagicFunc expects the second parameter to be passed by
reference, the std::thread constructor t doesn’t know that; it’s
oblivious to the types of the arguments expected by the function and
blindly copies the supplied values. When it calls Magicfunc, it will
end up passing a reference to the internal copy of data and not a
reference to data itself. Consequently, when the thread finishes,
these updates will be discarded as the internal copies of the supplied
arguments are destroyed, and process_widget_data will be passed an
unchanged Data myData rather than a correctly updated version.
但是,使用以下程序对此进行测试
#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
真的有必要,提供的信息有误吗?
谢谢
您没有直接更改 vecVals。迭代器正在工作,因为复制迭代器是可以的,它仍然指向相同的内存地址
您发布的代码在标准下实际上是非法的。 std::thread
应该使用参数的右值副本调用 AddValuesToSlots
。
一些 C++ 编译器会出错,而是使用参数的左值副本调用它。
测试编译器是否违反规则的简单方法是:
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
我正在通读 C++ 并发实战,在第 2 章中我被引导相信即使是函数原型,例如:
void MagicFunc(Data& myData);
旨在像下面这样使用:
Data dataExample;
thread t(MagicFunc,dataExample);
我真的应该这样做
Data dataExample
thread t(MagicFunc,std::ref(dataExample));
否则,我预计发生在 "dataExample" 上的变化将不会发生。具体来说,它是这样的:
Although MagicFunc expects the second parameter to be passed by reference, the std::thread constructor t doesn’t know that; it’s oblivious to the types of the arguments expected by the function and blindly copies the supplied values. When it calls Magicfunc, it will end up passing a reference to the internal copy of data and not a reference to data itself. Consequently, when the thread finishes, these updates will be discarded as the internal copies of the supplied arguments are destroyed, and process_widget_data will be passed an unchanged Data myData rather than a correctly updated version.
但是,使用以下程序对此进行测试
#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
真的有必要,提供的信息有误吗?
谢谢
您没有直接更改 vecVals。迭代器正在工作,因为复制迭代器是可以的,它仍然指向相同的内存地址
您发布的代码在标准下实际上是非法的。 std::thread
应该使用参数的右值副本调用 AddValuesToSlots
。
一些 C++ 编译器会出错,而是使用参数的左值副本调用它。
测试编译器是否违反规则的简单方法是:
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