为什么 for 循环和硬编码会产生不同的结果?
Why is the for loop and the hard coded yielding different results?
我用 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
.
std::thread([&]
[&]
表示闭包捕获的所有对象都通过引用捕获.
cl.run(maxLast, vals[i],i)
i
是通过引用捕获的。 i
是父执行线程的循环变量,在循环的每次迭代中都会递增。
当每个执行线程执行任何与父执行线程中的任何内容相关的任何内容时,C++ 不提供任何保证,除非显式 synchronization 发生。
这里没有发生这样的同步。到新执行线程评估此 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
。
这就是“通过引用方式”捕获对象。改为使用 [=]
按值 捕获 。这意味着每个执行线程在创建执行线程时看到每个捕获对象的值。
我用 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
.
std::thread([&]
[&]
表示闭包捕获的所有对象都通过引用捕获.
cl.run(maxLast, vals[i],i)
i
是通过引用捕获的。 i
是父执行线程的循环变量,在循环的每次迭代中都会递增。
当每个执行线程执行任何与父执行线程中的任何内容相关的任何内容时,C++ 不提供任何保证,除非显式 synchronization 发生。
这里没有发生这样的同步。到新执行线程评估此 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
。
这就是“通过引用方式”捕获对象。改为使用 [=]
按值 捕获 。这意味着每个执行线程在创建执行线程时看到每个捕获对象的值。