简体   繁体   English

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

[英]Using a shared pointer in a std::thread

I have a scenario where: 我有一种情况:

  1. I launch a new thread from within a dll that does some work. 我从执行某些工作的dll中启动一个新线程。

  2. The dlls destructor could be called before the new thread finishes its work. 可以在新线程完成其工作之前调用dll析构函数。

  3. If so I want to set a boolean flag in the destructor to tell the thread to return and not continue. 如果是这样,我想在析构函数中设置一个布尔标志,以告诉线程返回而不继续。

If I try the following then I find that because the destructor is called and MyDll goes out of scope then m_cancel is deleted and its value is unreliable (Sometimes false, sometimes true) so I cannot use this method. 如果尝试以下操作,则会发现由于调用了析构函数且MyDll超出范围,因此m_cancel被删除且其值不可靠(有时为false,有时为true),因此无法使用此方法。

Method 1 方法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();    
    }
}

So I have looked at this example Replacing std::async with own version but where should std::promise live? 因此,我看了这个示例, 将std :: async替换为自己的版本,但std :: promise应该放在哪里? where a shared pointer is used which can be accessed from both threads. 使用共享指针的地方,可以从两个线程访问该指针。

So I was thinking that I should: 所以我想我应该:

  1. Create a shared pointer to a bool and pass it to the new thread that I have kicked off. 创建一个指向布尔的共享指针,并将其传递给我开始的新线程。

  2. In the destructor, change the value of this shared pointer and check it in the new thread. 在析构函数中,更改此共享指针的值,然后在新线程中检查它。

Here is what I have come up with but I'm not sure if this is the proper way to solve this problem. 这是我想出的,但是我不确定这是否是解决此问题的正确方法。

Method 2 方法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 I do the above then the if( *m_Cancel.get() == true ) causes a crash (Access violation) 如果执行上述操作,则if(* m_Cancel.get()== true)导致崩溃(访问冲突)

  1. Do I pass the shared pointer by value or by reference to the std::thread?? 我是通过值还是通过引用std :: thread来传递共享指针?

  2. Because its a shared pointer, will the copy that the std::thread had still be valid even MyDll goes out of scope?? 因为它是一个共享指针,所以即使MyDll超出范围,std :: thread仍然有效的副本?

How can I do this?? 我怎样才能做到这一点??

Method 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();    
    }
}

What I fund is that when the destructor gets called and then if( *m_Cancel.get() == true ) is called it always crashes. 我资助的是,在调用析构函数然后调用if(* m_Cancel.get()== true)时 ,它总是崩溃。

Am I doing something wrong?? 难道我做错了什么??

检查m_Cancel时崩溃

Solution

I have added in a mutex to protect against the dtor returning after cancel has been checked in the new thread. 我添加了一个互斥锁,以防止在新线程中检查了取消之后返回的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();    
        }
    }
}

Step 2 is just wrong. 步骤2就是错误的。 That's a design fault. 那是设计错误。

Your first mechanism doesn't work for a simple reason. 您的第一个机制由于简单原因而无法工作。 m_cancel==false may be optimized out by the compiler. m_cancel==false可以由编译器优化。 When the destructor returns, m_cancel ceases to exist, and no statement in the destructor depends on that write. 当析构函数返回时, m_cancel不再存在,并且析构函数中没有任何语句依赖于该写操作。 After the destructor returns, it would be Undefined Behavior to access the memory which previously held m_cancel . 析构函数返回后,访问先前持有m_cancel的内存将为Undefined Behavior。

The second mechanism (global) fails for a more complex reason. 第二种机制(全局机制)由于更复杂的原因而失败。 There's the obvious problem that you have only one global m_Cancel (BTW, m_ is a really misleading prefix for something that's not a member). 一个明显的问题是,您只有一个全局m_Cancel (顺便说一句, m_是一个不属于成员的东西的确实令人误解的前缀)。 But assuming you only have one MyDll , it can still fail for threading reasons. 但是,假设您只有一个MyDll ,由于线程原因它仍然可能失败。 What you wanted was not a shared_ptr but a std::atomic<bool> . 您想要的不是shared_ptr而是std::atomic<bool> That is safe for access from multiple threads 从多个线程访问是安全的

[edit] And your third mechanism fails because [=] captures names from the enclosing scope. [edit]第三种机制失败了,因为[=]从封闭范围捕获名称。 m_Cancel isn't in that scope, but this is. m_Cancel是不是在该范围内,但是this是。 You don't want a copy of this for the thread though, because this will be destroyed. 你不想要的副本this对线程,虽然,因为this将被销毁。 Solution: auto cancel = this->m_Cancel; std::thread([cancel](... 解决方案: auto cancel = this->m_Cancel; std::thread([cancel](... auto cancel = this->m_Cancel; std::thread([cancel](...

[edit 2] I think you really should read up on basics. [编辑2]我认为您确实应该阅读基础知识。 In the dtor of version 3, you indeed change the value of m_cancel . 在版本3的dtor中,您确实更改了m_cancel的值。 That is to say, you change the pointer. 也就是说,您更改了指针。 You should have changed *m_cancel , ie what it points to. 您应该已经更改了*m_cancel ,即它指向的内容。 As I pointed out above, the thread has a copy of the pointer. 正如我在上面指出的,线程具有指针的副本 If you change the original pointer, the thread will continue to point to the old value. 如果更改原始指针,则线程将继续指向旧值。 (This is unrelated to smart pointers, dumb pointers behave the same). (这与智能指针无关,哑指针的行为相同)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM