这个代码线程安全吗?

时间:2012-05-20 14:05:01

标签: c++ multithreading thread-safety mutex

我正在重构一些耗时的函数,以便可以从一个线程调用它,但是我无法解决问题(不熟悉线程编程)。

在任何时候,用户都可以取消,该功能将停止。我不想在用户取消后立即终止线程,因为它可能会导致一些数据完整性问题。相反,在函数的几个地方,我将检查函数是否已被取消,如果是,则退出。我只会这样做,我知道退出是安全的。

该函数的整个代码将在互斥锁中。这是我想到的伪代码:

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()到达可以安全退出的点时,它将退出。

知道这是否都是线程安全的?

3 个答案:

答案 0 :(得分:3)

您应该使用其他互斥锁锁定cancelled_的访问权限,因此检查和设置不会同时发生。除此之外,我认为你的方法还可以

更新:此外,请确保SomeClass::bigSearch()不会抛出任何异常,否则互斥锁可能会保持锁定状态。要确保所有返回路径都解锁互斥锁,您可能希望使用if (!cancelled_)包围代码的处理部分,并仅在方法的最后返回(您有一个unlock()调用在互斥上。

更好的是,将互斥锁包装在RAII(资源分配是初始化的首字母缩写)对象中,因此无论函数如何结束(异常或其他),都可以保证互斥锁被解锁。

答案 1 :(得分:2)

是的,这是线程安全的。但是:

  1. 处理器可以有单独的缓存并缓存它自己的cancelled_副本,通常互斥同步功能会应用正确的缓存同步。
  2. 编译器生成的代码可以对您的数据位置做出无效假设,这可能导致不及时更新cancelled_。某些特定于平台的命令可以在这里提供帮助,或者您可以简单地使用其他机制。
  3. 所有这些导致一个未按照您的意愿及时取消的帖子。

    您的代码使用模式很简单“信令”。所以你需要将信号传递给线程。信号模式允许触发多次相同的触发(信号),并在以后清除它。

    可以使用以下方法模拟:

    • 原子作业
    • 互斥锁保护变量
    • 信号同步原语

答案 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;
}