我目前正在开发一个多线程服务器(使用c ++ 14),我正面临一种奇怪的问题,让我解释一下:
所以这里我只是用lambda表达式创建nb_threads线程并将它们放在forward_list中:
unsigned nb_threads = 4;
std::forward_list<std::thread> threads;
for (unsigned i = 0; i < nb_threads; ++i)
threads.emplace_front(std::thread(
[i, this]()
{
std::cout << "Thread " << i + 1 << " launched!" << std::endl;
}));
这给了我stdout上的结果(正确):
Thread 2 launched!
Thread 3 launched!
Thread 4 launched!
Thread 1 launched!
现在让我们在lambda表达式中通过[&amp;] 更改[i,this]:
for (unsigned i = 0; i < nb_threads; ++i)
threads.emplace_front(std::thread(
[&]()
{
std::cout << "Thread " << i + 1 << " launched!" << std::endl;
}));
这现在给了我stdout的结果(真的是 mystic !):
Thread 2 launched!
Thread 1 launched!
Thread 1 launched!
Thread 1 launched!
所以我的问题是发生了什么?为什么我不能改变 [&amp;] ?
答案 0 :(得分:3)
随着i
的值发生变化,对i
的引用值会随之变化。由于您没有同步,因此这些更改是不可预测的。在这里,您显然希望按值捕获i
。
答案 1 :(得分:3)
这不再是“神秘”而是
之间的区别void thread_body (int i) {}
和
void thread_body (int &i) {}
在第一种情况下,参数的当前值被传递给线程,除非函数本身修改它,否则我不会改变。
在第二个中,你传递一个外部变量的引用,该外部变量可能在函数控件之外的任何时候发生变化。
这里唯一神秘的东西是血腥可怕的C ++语法,但是既然你正在编写一个多任务C ++ 14服务器,你很快就会习惯它。
答案 2 :(得分:2)
通过引用捕获i
时,此处发生的是数据竞赛。通过引用捕获时,每个lambda将获得与引用捕获的变量相同的内存位置的引用。
基本上发生的事情是主线程正在写入i
而生成的线程试图从同一个变量读取而没有同步。
在没有同步的情况下同时执行对同一内存位置的读取和写入将导致数据竞争并表现出不确定的行为。