在std :: thread中使用共享指针

时间:2015-03-04 11:16:19

标签: c++ multithreading pointers c++11

我有一个场景:

  1. 我从dll中启动了一个新线程,可以完成一些工作。

  2. 可以在新线程完成其工作之前调用dll析构函数。

  3. 如果是这样,我想在析构函数中设置一个布尔标志,告诉线程返回而不是继续。

  4. 如果我尝试以下操作,那么我发现因为析构函数被调用而MyDll超出范围,所以 m_cancel 被删除,其值不可靠(有时是假的,有时是真的)所以我不能使用这种方法。

    方法1

    //member variable declared in header file
    bool m_cancel = false;
    MyDll:~MyDll()
    {
        m_cancel = true;
    }
    
    //Function to start receiving data asynchronously  
    void MyDll::GetDataSync()
    {
        std::thread([&]() 
        {
            SomeFunctionThatCouldTakeAWhile();
    
            if( m_cancel == true )
              return;
    
            SomeFunctionThatDoesSomethingElse();    
        }
    }
    

    所以我看了一下这个例子Replacing std::async with own version but where should std::promise live?,其中使用了一个可以从两个线程访问的共享指针。

    所以我想我应该:

    1. 创建一个指向bool的共享指针,并将其传递给我已经启动的新线程。

    2. 在析构函数中,更改此共享指针的值并在新线程中进行检查。

    3. 以下是我的想法,但我不确定这是否是解决此问题的正确方法。

      方法2

      //member variable declared in header file
      std::shared_ptr<bool> m_Cancel;
      
      //Constructor
       MyDll:MyDll()
      {
          m_Cancel = make_shared<bool>(false);
      }
      //Destructor
      MyDll:~MyDll()
      {
          std::shared_ptr<bool> m_cancelTrue = make_shared<bool>(true);
      
          m_Cancel = std::move(m_cancelTrue);
      }
      
      //Function to start receiving data asynchronously  
      void MyDll::GetDataSync()
      {
          std::thread([&]() 
          {
              SomeFunctionThatCouldTakeAWhile();
      
              if( *m_Cancel.get() == true )
                  return;
      
              SomeFunctionThatDoesSomethingElse();    
          }
      }
      

      如果我执行上述操作,那么 if(* m_Cancel.get()== true)会导致崩溃(访问冲突)

      1. 我是按值传递共享指针还是通过引用传递给std :: thread ??

      2. 因为它是一个共享指针,即使MyDll超出范围,std :: thread仍然有效的副本吗?

      3. 我该怎么做?

        方法3

        //Members declared in header file
        std::shared_ptr<std::atomic<bool>> m_Cancel;
        
        //Constructor
         MyDll:MyDll()
        {
            //Initialise m_Cancel to false
            m_Cancel = make_shared<std::atomic<bool>>(false);
        }
        //Destructor
        MyDll:~MyDll()
        {
            //Set m_Cancel to true
            std::shared_ptr<std::atomic<bool>> m_cancelTrue = make_shared<std::atomic<bool>>(true);
        
            m_Cancel = std::move(m_cancelTrue);
        }
        
        //Function to start receiving data asynchronously  
        void MyDll::GetDataSync()
        {
            std::thread([=]() //Pass variables by value
            {
                SomeFunctionThatCouldTakeAWhile();
        
                if( *m_Cancel.get() == true )
                    return;
        
                SomeFunctionThatDoesSomethingElse();    
            }
        }
        

        我资助的是当析构函数被调用然后 if(* m_Cancel.get()== true)被调用时,它总是崩溃。

        我做错了吗?

        Crash when checking m_Cancel

        解决方案

        我添加了一个互斥锁,以防止在新线程中检查取消后dtor返回。

        //Members declared in header file
        std::shared_ptr<std::atomic<bool>> m_Cancel;
        std::shared_ptr<std::mutex> m_sharedMutex;
        
        //Constructor
         MyDll:MyDll()
        {
            //Initialise m_Cancel to false
            m_Cancel = make_shared<std::atomic<bool>>(false);
            m_sharedMutex = make_shared<std::mutex>();
        }
        //Destructor
        MyDll:~MyDll()
        {
            //Set m_Cancel to true
            std::shared_ptr<std::atomic<bool>> m_cancelTrue = make_shared<std::atomic<bool>>(true);
        
            std::lock_guard<std::mutex> lock(*m_sharedMutex);//lock access to m_Cancel
            {
                *m_Cancel = std::move(cancelTrue);
            }
        }
        
        //Function to start receiving data asynchronously  
        void MyDll::GetDataSync()
        {
            auto cancel = this->m_Cancel;
            auto mutex = this->m_sharedMutex;
        
            std::thread([=]() //Pass variables by value
            {
                SomeFunctionThatCouldTakeAWhile();
        
                std::lock_guard<std::mutex> lock(*mutex);//lock access to cancel
                {
                    if( *cancel.get() == true )
                        return;
        
                    SomeFunctionThatDoesSomethingElse();    
                }
            }
        }
        

1 个答案:

答案 0 :(得分:3)

第2步是错的。这是一个设计错误。

你的第一个机制并不是因为一个简单的原因而起作用的。 m_cancel==false可以由编译器优化。当析构函数返回时,m_cancel不再存在,并且析构函数中的语句不依赖于该写入。析构函数返回后,访问以前保存m_cancel的内存将是Undefined Behavior。

第二种机制(全局)因更复杂的原因而失败。显而易见的问题是,您只有一个全局m_Cancel(BTW,m_对于某些不属于某个成员的内容来说是一个非常具有误导性的前缀。但假设您只有一个MyDll,它仍然可能因线程原因而失败。你想要的不是shared_ptr而是std::atomic<bool>。这对于从多个线程访问是安全的

[编辑] 并且您的第三个机制失败,因为[=]从封闭范围中捕获名称。 m_Cancel不在该范围内,但this是。this。您不希望该帖子的this副本,因为auto cancel = this->m_Cancel; std::thread([cancel](...将被销毁。解决方案:m_cancel

[编辑2] 我认为你真的应该阅读基础知识。在版本3的dtor中,您确实更改了*m_cancel的值。也就是说,你改变了指针。您应该更改{{1}},即它指向的内容。正如我在上面指出的那样,线程有一个指针的副本。如果更改原始指针,则线程将继续指向旧值。 (这与智能指针无关,哑指针表现相同)。