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

时间:2016-03-15 19:13:37

标签: c++ multithreading c++11

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

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期间可能发生什么问题(哪些情况没有得到正确处理?)?

2 个答案:

答案 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_guardstd::unique_lock

答案 1 :(得分:0)

规则如下:

  1. 从不同的线程读取和写入相同的变量是未定义的行为。
  2. 在没有任何锁定或原子操作的情况下同时读取和写入相同的变量而不是可能会导致内存不可见。一个线程可能无法“读取”其他线程写入的最新值
  3. 从两个不同的线程读取相同的变量是线程安全的。
  4. 问题:

      

    //做一些需要this指针的东西。

    如果“stuff”仅从this指向的成员读取,则整个锁定是多余的。
    另一方面,如果一个线程在某种情况下改变this,那么锁定是强制性的。在这种情况下,锁使并发操作成为线程安全的

      

    //做一些不需要this指针的东西。

    再一次,如果两个线程中唯一的动作是读取,那么该方法是线程安全的。如果一个线程正在写入变量(,无论它是否是成员变量!线程只看到内存地址)而另一个正在读取/写入它,那么你必须锁定该变量或者使用原子。

      

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

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

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