我正在尝试编写一个程序,该程序可以同时添加和删除“仓库”中的项目。我有一个处理“仓库”操作的“监视器”类:
class Monitor
{
private:
mutex m;
condition_variable cv;
vector<Storage> S;
int counter = 0;
bool busy = false;;
public:
void add(Computer c, int index) {
unique_lock <mutex> lock(m);
if (busy)
cout << "Thread " << index << ": waiting for !busy " << endl;
cv.wait(lock, [&] { return !busy; });
busy = true;
cout << "Thread " << index << ": Request: add " << c.CPUFrequency << endl;
for (int i = 0; i < counter; i++) {
if (S[i].f == c.CPUFrequency) {
S[i].n++;
busy = false; cv.notify_one();
return;
}
}
Storage s;
s.f = c.CPUFrequency;
s.n = 1;
// put the new item in a sorted position
S.push_back(s);
counter++;
busy = false; cv.notify_one();
}
}
创建线程的方式如下:
void doThreadStuff(vector<Computer> P, vector <Storage> R, Monitor &S)
{
int Pcount = P.size();
vector<thread> myThreads;
myThreads.reserve(Pcount);
for (atomic<size_t> i = 0; i < Pcount; i++)
{
int index = i;
Computer c = P[index];
myThreads.emplace_back([&] { S.add(c, index); });
}
for (size_t i = 0; i < Pcount; i++)
{
myThreads[i].join();
}
// printing results
}
运行程序产生以下结果:
我对比赛条件很熟悉,但是对我来说这并不像。我的赌注将放在与参考相关的东西上,因为在结果中我们可以看到,对于每个“缺少线程”(线程1、3、10、25),我都会得到“重复线程”(线程2、9、24、28)。
我试图在函数和循环中创建局部变量,但是它什么也没改变。
我听说过线程共享内存区域,但是我以前的工作应该产生类似的结果,所以我认为不是这种情况,但是可以随时证明我是错误的。
我正在使用Visual Studio 2017
答案 0 :(得分:7)
在这里您通过循环引用捕获局部变量,它们将在任何时候被破坏,从而导致未定义的行为:
for (atomic<size_t> i = 0; i < Pcount; i++)
{
int index = i;
Computer c = P[index];
myThreads.emplace_back([&] { S.add(c, index); });
}
您应该按值捕获index
和c
:
myThreads.emplace_back([&S, index, c] { S.add(c, index); });
答案 1 :(得分:1)
另一种方法是将S
,i
和c
作为参数传递,而不是通过定义以下 non-captureing lambda {{ 1}}:
th_func
通过这种方式,您必须通过功能模板std::reference_wrapper
,明确地包装必须通过引用传递到线程的可调用对象的参数。 {3}}。就您而言,只有auto th_func = [](Monitor &S, int index, Computer c){ S.add(c, index); };
:
S
未能用for (atomic<size_t> i = 0; i < Pcount; i++) {
int index = i;
Computer c = P[index];
myThreads.emplace_back(th_func, std::ref(S), index, c);
}
包装必须通过引用传递的参数将导致编译时错误。也就是说,以下代码不会编译:
std::reference_wrapper
另请参阅std::ref()
。