使用操作当前对象的线程是否安全

Is it safe to use thread that manipulate the current object?

本文关键字:线程 是否 安全 对象 操作      更新时间:2023-10-16

>想象一下我有以下内容:

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

上面的编译和工作(使用更复杂的实现)。

  1. 它是否安全(如果不是,我怎样才能使其安全?

  2. _work期间可能会发生什么错误(哪些案件没有得到正确处理?

当前的实现有一个微妙的错误。我想知道您是否在实际代码中也有它。

 while (true) {
       _mutex.lock();
       // get_next() modify the internal state of this
      if (!get_next()) break;
       _mutex.unlock();

在这里,中断将退出循环并保持互斥锁锁定状态。欢迎来到死锁!为了解决这个微妙的问题,我真的建议避免使用 mutex.lock()/unlock() .相反,应该使用std::lock_guardstd::unique_lock

规则如下:

  1. 从不同的线程读取和写入同一变量是未定义的行为。
  2. 没有任何锁定或原子操作的情况下同时读取和写入同一变量可能会导致内存不可见。 一个线程可能不会"读取"另一个线程写入的最新值
  3. 从两个不同的线程读取相同的变量是线程安全的。

对于您的问题:

做一些需要this指针的事情。

如果"东西"只是从this指出的成员那里读取,那么整个锁定是多余的。
另一方面,如果一个线程以某种方式更改this,则锁定是强制性的。在这种情况下,锁使并发操作是线程安全的

做一些不需要this指针的事情。

同样,如果在两个线程中执行的唯一操作是读取,则该方法是线程安全的。 如果一个线程正在写入变量(无论它是否是成员变量!线程只能看到内存地址),而另一个线程正在读取/写入它,则必须锁定该变量或为其使用原子。

_work期间可能发生什么错误(哪些情况不正确) 处理?

在行为未定义的土地上,任何事情都可能发生。 例如,当您取消引用无效内存地址时会发生什么?

PS. 使用标准 RAII 包装器锁定互斥锁,不要自己手动锁定。