【问题标题】:Why is the for loop and the hard coded yielding different results?为什么 for 循环和硬编码会产生不同的结果?
【发布时间】:2022-01-24 03:03:09
【问题描述】:

我在 C++ 中创建了一个程序,当使用 for 循环创建线程向量的组件时它不起作用,而当它们被硬编码时它会起作用。 这是硬编码的例子:

std::vector<std::thread> ThreadVector;
ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[0], 0);})));
ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[1],1);})));
ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[2],2);})));
ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[3],3);})));
ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[4],4);})));

for(auto& t:ThreadVector)
        t.join();

maxLast 是一个整数,它是双字符数组的值。 vals 变量是一个三元组字符数组,考虑一个向量中的字符串向量。最后一个值是通过函数发送的数字。 cl.run 函数基本上只需要一个 char 数组,将 maxLast 变量写入文件,然后在下一行写入数组中的下一个字符串。文件名叫trying加号再.txt函数如下图:

void dir::run(int nums, char *vals[nums], int count){
    std::cout<<"got started "<<nums<<" "<<count<<std::endl;
    std::string filestr = "trying"+std::to_string(count)+".txt";
    for(int i = 0; i < nums; i++){
        writeLine(std::to_string(nums), filestr);
        writeLine(std::string(vals[i]), filestr);
    }
}

void dir::writeLine(std::string w, std::string p){
    FILE* stream = fopen(p.c_str(), "a+");  
    fputs(w.c_str(), stream);
    fputs("\n", stream);
    fclose(stream);

}

当硬编码到如上所示的ThreadVector 变量中运行时,它可以完美运行。但是,如果我按以下方式运行它,将会有一个名为 trying5.txt 的文件,还会有我从未放入打印到文件上的 char 数组中的字符串。也会有一些线程收到相同的i 值。

int i;
int ques=5;
for(i = 0; i < ques; i++){
    printf("running %d\n", i);
    ThreadVector.emplace_back(std::move(std::thread([&](){cl.run(maxLast, vals[i],i);})));
}
for(auto& t:ThreadVector)
        t.join();

在其中一个运行中打印到终端(清理了一下):

running 0
running 1
running 2
running 3
running 4
got started 30 2
got started 30 2
got started 30 4
got started 30 3
got started 30 4

这显然不是应该发生的,正如提到的,有时终端会打印got started 30 5,我最终会得到一个名为trying5.txt的文件。我也试过push_back而不是emplace_back

【问题讨论】:

  • 多线程不仅仅是知道如何创建线程。您发布的程序中根本没有同步,因此无法保证将执行什么顺序。您无法使用单线程方法或思维方式来处理多线程编程。
  • 您通过引用捕获ii 的值是不可预测的,因为线程的开始时间是不确定的。
  • 考虑使用 std::async 代替 std::thread。并不是说那会避免你的“问题”。对于线程,您确实需要知道何时需要为线程提供自己的数据副本以进行处理,或者何时需要共享数据访问权限。在编写线程安全代码之前,您可能想了解按值/引用捕获、std::atomic、std::mutex、std::unique_lock、std::condition_variable、std::shared_ptr 等。至少你得到了 lambda 位,这是一个好的开始 :)

标签: c++ arrays multithreading c++17


【解决方案1】:
std::thread([&]

[&amp;] 表示所有被闭包捕获的对象都被通过引用捕获。

cl.run(maxLast, vals[i],i)

i 被引用捕获。 i 是父执行线程的循环变量,每次循环迭代都会递增。

C++ 不保证每个执行线程何时执行任何与父执行线程中的任何内容相关的任何内容,除非发生显式同步。 p>

这里没有发生这样的同步。在新的执行线程评估此val[i] 以及函数调用的离散i 参数时,父执行线程可能已经增加了i;它已经结束了循环的当前迭代并移至下一个迭代。甚至多次迭代。或者循环可能在父执行线程中完全结束,i 现在处于其最终值。

got started 30 2
got started 30 2
got started 30 4
got started 30 3
got started 30 4

您可以在此处准确看到此结果。在父执行线程迭代并递增i 之后,每个执行线程都开始评估其i,次数不可预测。一些新的执行线程甚至设法同时评估i,导致i 的观察值相同。

这就是“通过引用”捕获对象的方式。请改用[=] 来捕获按值。这意味着每个执行线程在创建执行线程时都会看到每个捕获对象的值。

【讨论】:

  • 这不仅仅是“可能已经”;这是未定义的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多