带有 lambda [&] 的线程导致神秘的结果

Threads with lambda [&] leading to mystic results

我目前正在开发一个多线程服务器(使用 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;
            }));

这在标准输出上给出了这个结果(正确):

Thread 2 launched!
Thread 3 launched!
Thread 4 launched!
Thread 1 launched!

现在让我们只需在 lambda 表达式中将 [i, this] 更改为 [&]

for (unsigned i = 0; i < nb_threads; ++i)
      threads.emplace_front(std::thread(
            [&]()
            {
              std::cout << "Thread " << i + 1 << " launched!" << std::endl;
            }));

这现在在标准输出上给了我这个结果(这真的 mystic!):

Thread 2 launched!
Thread 1 launched!
Thread 1 launched!
Thread 1 launched!

所以我的问题是 发生了什么事? 为什么我不能更改 [&]

随着i值的变化,对i的引用值也随之变化。由于您没有同步,因此此类更改是不可预测的。在这里,您显然希望按值捕获 i

这与

之间的差异不超过 "mystic"
void thread_body (int i) {}

void thread_body (int &i) {}

在第一种情况下,参数的当前值被传递给线程,除非函数本身修改它,否则我不会更改它。

在第二个过程中,您传递了对某些外部变量的引用,该变量可能会在函数的控制之外随时更改。

这里唯一神秘的是该死的可怕的 C++ 语法,但由于您正在编写多任务 C++14 服务器,您很快就会习惯它。

竞争条件

通过引用捕获 i 时发生的是 数据竞争 。通过引用捕获时,每个 lambda 将获得与通过引用捕获的变量相同的内存位置的引用。

基本上发生的事情是主线程正在写入 i 而派生线程正在尝试从同一个变量中读取,没有同步。

在没有同步的情况下同时对同一内存位置执行读取和写入将导致数据竞争并表现出不确定的行为。