想象一下,我有以下内容:
struct A {
std::vector<int> _vect;
std::mutex _mutex;
void f () {
std::thread t1(&A::work, this);
std::thread t2(&A::work, this);
t1.join();
t2.join();
}
void work () {
std::vector<int> cvect;
while (true) {
bool keep = false;
_mutex.lock();
// get_next() modify the internal state of this
keep = get_next();
cvect = _vect; // copy of _vect
_mutex.unlock();
if (!keep) break;
// Do some stuff that does not require the `this`, e.g.:
std::sort(cvect.begin(), cvect.end());
int v = cvect.back() - cvect.front();
}
}
bool get_next () {
// This methods modify _vect
_vect = std::vector<int>{1, 2, 3, 4}; // e.g.
}
}
int main () {
A a;
a.f();
return 0;
}
以上编译和工作(使用更复杂的实现)。
是否安全(如果没有,我怎样才能安全?)?
_work
期间可能发生什么问题(哪些情况没有得到正确处理?)?
答案 0 :(得分:4)
当前的实现有一个微妙的错误。我想知道你的真实代码中是否也有它。
while (true) {
_mutex.lock();
// get_next() modify the internal state of this
if (!get_next()) break;
_mutex.unlock();
此处中断将退出循环并锁定互斥锁。欢迎来到僵局!要解决这个微妙的问题,我建议您避免使用mutex.lock()
/ unlock()
。相反,应该使用std::lock_guard
或std::unique_lock
。
答案 1 :(得分:0)
规则如下:
问题:
//做一些需要
this
指针的东西。
如果“stuff”仅从this
指向的成员读取,则整个锁定是多余的。
另一方面,如果一个线程在某种情况下改变this
,那么锁定是强制性的。在这种情况下,锁使并发操作成为线程安全的
//做一些不需要
this
指针的东西。
再一次,如果两个线程中唯一的动作是读取,那么该方法是线程安全的。如果一个线程正在写入变量(,无论它是否是成员变量!线程只看到内存地址)而另一个正在读取/写入它,那么你必须锁定该变量或者使用原子。
_work期间可能发生什么问题(哪些情况不正确 处理?)?
在未定义行为的土地上,任何事情都可能发生。例如,当您取消引用无效的内存地址时会发生什么?
PS。使用标准的RAII包装来锁定互斥锁,不要自己手动锁定它。