我正在重构一些耗时的函数,以便可以从一个线程调用它,但是我无法解决问题(不熟悉线程编程)。
在任何时候,用户都可以取消,该功能将停止。我不想在用户取消后立即终止线程,因为它可能会导致一些数据完整性问题。相反,在函数的几个地方,我将检查函数是否已被取消,如果是,则退出。我只会这样做,我知道退出是安全的。
该函数的整个代码将在互斥锁中。这是我想到的伪代码:
SomeClass::SomeClass() {
cancelled_ = false;
}
void SomeClass::cancelBigSearch() {
cancelled_ = true;
}
void SomeClass::bigSearch() {
mutex.lock();
// ...
// Some code
// ...
// Safe to exit at this point
if (cancelled_) {
mutex.unlock();
cancelled_ = false;
return;
}
// ...
// Some more code
// ...
if (cancelled_) {
mutex.unlock();
cancelled_ = false;
return;
}
// ...
// Again more code
// ...
if (cancelled_) {
mutex.unlock();
cancelled_ = false;
return;
}
mutex.unlock();
}
因此,当用户开始搜索时,新线程会调用bigSearch()
。如果用户取消,则调用cancelBigSearch()
并设置cancelled_
标志。然后,当bigSearch()
到达可以安全退出的点时,它将退出。
知道这是否都是线程安全的?
答案 0 :(得分:3)
您应该使用其他互斥锁锁定cancelled_
的访问权限,因此检查和设置不会同时发生。除此之外,我认为你的方法还可以
更新:此外,请确保SomeClass::bigSearch()
不会抛出任何异常,否则互斥锁可能会保持锁定状态。要确保所有返回路径都解锁互斥锁,您可能希望使用if (!cancelled_)
包围代码的处理部分,并仅在方法的最后返回(您有一个unlock()
调用在互斥上。
更好的是,将互斥锁包装在RAII(资源分配是初始化的首字母缩写)对象中,因此无论函数如何结束(异常或其他),都可以保证互斥锁被解锁。
答案 1 :(得分:2)
是的,这是线程安全的。但是:
cancelled_
副本,通常互斥同步功能会应用正确的缓存同步。cancelled_
。某些特定于平台的命令可以在这里提供帮助,或者您可以简单地使用其他机制。所有这些导致一个未按照您的意愿及时取消的帖子。
您的代码使用模式很简单“信令”。所以你需要将信号传递给线程。信号模式允许触发多次相同的触发(信号),并在以后清除它。
可以使用以下方法模拟:
答案 2 :(得分:1)
它是不线程安全,因为一个线程可以在另一个线程写入它的同时读取cancelled_
,这是一个数据争用,这是未定义的行为。
正如其他人所建议的那样,要么使用cancelled_
的原子类型,要么用另一个互斥锁保护它。
您还应该使用RAII类型来锁定互斥锁。
e.g。
void SomeClass::cancelBigSearch() {
std::lock_guard<std::mutex> lock(cxlMutex_);
cancelled_ = true;
}
bool SomeClass::cancelled() {
std::lock_guard<std::mutex> lock(cxlMutex_);
if (cancelled_) {
// reset to false, to avoid caller having to lock mutex again to reset it
cancelled_ = false;
return true;
}
return false;
}
void SomeClass::bigSearch() {
std::lock_guard<std::mutex> lock(mutex);
// ...
// Some code
// ...
// Safe to exit at this point
if (cancelled())
return;
// ...
// Some more code
// ...
if (cancelled())
return;
// ...
// Again more code
// ...
if (cancelled())
return;
}