【发布时间】:2015-05-05 08:37:50
【问题描述】:
我有一个场景:
我从一个 dll 中启动了一个新线程,该线程执行了一些工作。
可以在新线程完成工作之前调用 dll 析构函数。
如果是这样,我想在析构函数中设置一个布尔标志来告诉线程返回而不是继续。
如果我尝试以下操作,我发现因为调用了析构函数并且 MyDll 超出了范围,然后 m_cancel 被删除并且它的值不可靠(有时为假,有时为真)所以我不能使用这个方法。
方法一
//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?,其中使用了可以从两个线程访问的共享指针。
所以我想我应该:
创建一个指向 bool 的共享指针并将其传递给我启动的新线程。
在析构函数中,改变这个共享指针的值,并在新线程中检查。
这是我想出的,但我不确定这是否是解决此问题的正确方法。
方法二
//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 ) 会导致崩溃(访问冲突)
我是通过值还是通过引用 std::thread 来传递共享指针?
因为它是一个共享指针,即使 MyDll 超出范围,std::thread 所拥有的副本仍然有效吗??
我该怎么做??
方法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 ) 时,它总是崩溃。
我是不是做错了什么?
解决方案
我添加了一个互斥锁,以防止在新线程中检查取消后 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();
}
}
}
【问题讨论】:
-
你能在你的析构函数中等待线程结束吗?
-
如果我等待,那么有一个 UI 将被阻止长达 20 秒。我想不惜一切代价避免这种情况。
-
您如何在您的
MyDll中使用std::atomic<bool> flag来进行通信以中断线程。然后线程的函数会定期检查这个flag,如果它设置为真则返回。在MyDll的析构函数中,您将flag设置为true,然后将join()您的线程(等待其终止)设置为不再需要很长时间,因为您发送了中断信号。 -
我面临的挑战是我的函数 SomeFunctionThatCouldTakeAWhile 是 Curl 同步 curl_easy_perform 操作,无法取消此操作。唯一的选择是让它超时。如果我等待这个超时,那么它太长了,这就是我想开火并忘记的原因。如果我 join() 那么它会导致延迟。
-
是否有任何我可以使用的方法/设计让 MyDll 超出范围并让我的 std::thread 检测到这一点并简单地返回而不继续?
标签: c++ multithreading pointers c++11