0

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

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

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

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

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

4

3 回答 3

3

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

更新:另外,请确保不会抛出异常SomeClass::bigSearch(),否则互斥锁可能会保持锁定状态。为确保所有返回路径解锁互斥锁,您可能希望将代码的处理部分包围起来,if (!cancelled_)并仅在方法的最后返回(您unlock()对互斥锁进行了一次调用)。

更好的是,将互斥锁包装在 RAII(Resource Allocation Is Initialization的首字母缩写词)对象中,因此无论函数如何结束(异常或其他),互斥锁都可以保证被解锁。

于 2012-05-20T14:29:13.030 回答
2

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

  1. 处理器可以有单独的缓存和缓存它自己的副本cancelled_,通常互斥同步函数应用适当的缓存同步。
  2. 编译器生成的代码,可能会对您的数据局部性做出无效假设,这可能导致无法及时更新cancelled_。一些特定于平台的命令可以在这里提供帮助,或者您可以简单地使用其他机制。

所有这些都会导致线程没有按您的意愿及时取消。

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

这可以使用以下方法进行模拟:

  • 原子操作
  • 互斥保护变量
  • 信号同步原语
于 2012-05-20T14:46:32.440 回答
1

不是线程安全的,因为一个线程可以cancelled_同时读取另一个线程写入它,这是一个数据竞争,这是未定义的行为。

正如其他人所建议的那样,要么使用原子类型,要么使用cancelled_另一个互斥锁来保护它。

您还应该使用 RAII 类型来锁定互斥锁。

例如

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;
}
于 2012-05-20T23:46:16.190 回答