[英]Using a shared pointer in a std::thread
I have a scenario where: 我有一种情况:
I launch a new thread from within a dll that does some work. 我从执行某些工作的dll中启动一个新线程。
The dlls destructor could be called before the new thread finishes its work. 可以在新线程完成其工作之前调用dll析构函数。
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: 所以我想我应该:
Create a shared pointer to a bool and pass it to the new thread that I have kicked off. 创建一个指向布尔的共享指针,并将其传递给我开始的新线程。
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)导致崩溃(访问冲突)
Do I pass the shared pointer by value or by reference to the std::thread?? 我是通过值还是通过引用std :: thread来传递共享指针?
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?? 难道我做错了什么??
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.