循环内的c ++线程输出错误的值


19

我试图理解c ++中的多线程,但是我陷入了这个问题:如果我在for循环中启动线程,它们会打印错误的值。这是代码:

#include <iostream>
#include <list>
#include <thread>

void print_id(int id){
    printf("Hello from thread %d\n", id);
}

int main() {
    int n=5;
    std::list<std::thread> threads={};
    for(int i=0; i<n; i++ ){
        threads.emplace_back(std::thread([&](){ print_id(i); }));
    }
    for(auto& t: threads){
        t.join();
    }
    return 0;
}

我原本希望打印出0、1、2、3、4的值,但是我经常两次得到相同的值。这是输出:

Hello from thread 2
Hello from thread 3
Hello from thread 3
Hello from thread 4
Hello from thread 5

我缺少什么?


7
i按值传递给lambda ,[i]
rafix07

1
值得注意的是,您对的使用emplace_back是奇怪的:emplace_back接受参数列表并将其传递给的构造函数std::thread。您传递了的(右值)实例std::thread,因此将构造一个线程,然后将该线程移入向量。通过更常用的方法可以更好地表达该操作push_back。编写threads.emplace_back([i](){ print_id(i); });(就地构建)或threads.push_back(std::thread([i](){ print_id(i); }));(构建+移动)较为习惯的做法会更明智。
米洛·布兰特

Answers:


17

[&]语法导致被引用i捕获。因此,线程运行时通常会比您预期的更高级。更严重的是,如果在线程运行之前超出范围,则代码的行为是不确定的ii

i通过价值捕获-即std::thread([i](){ print_id(i); })是解决办法。


2
或使用较少或不常建议std::thread([=](){ print_id(i); })
Wander3r

3
该行为尚未定义,因为这是(非原子)i主线程写入和其他线程读取的数据竞争。
胡桃

6

两个问题:

  1. 您无法控制线程何时运行,这意味着ilambda中变量的值可能不是您所期望的。

  2. 该变量i在循环中是局部的,并且仅在循环中。如果循环在一个或多个线程运行之前完成,则这些线程将无效引用其生存期已结束的变量。

通过i 按值而不是按引用捕获变量可以非常简单地解决这两个问题。这意味着每个线程将拥有该值的副本,并且该副本将为每个线程唯一。


5

另一件事:
不要等到总是有序的序列:0、1、2、3,...,因为多线程执行模式具有特定性:不确定性

不确定性意味着在相同条件下执行同一程序会产生不同的结果。

这是由于操作系统根据多个参数将线程从一个执行到另一个执行的调度时间不同的事实:CPU负载,其他进程的优先级,可能的系统中断,...

您的示例仅包含5个线程,因此很简单,尝试增加线程数,例如在处理函数中添加一个睡眠,您将看到结果在一个执行与另一个执行之间可能是不同的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.