这是我的代码:
//Interface class to derive from
class ICurlCallbackHandler
{
public:
virtual size_t CurlDataCallback( void* pData, size_t tSize ) = 0;
};
//Class that implements interface
class CurlCallbackHandler : public ICurlCallbackHandler
{
public:
bool m_exit = false;
virtual size_t CurlDataCallback( void* pData, size_t tSize ) override
{
if(m_exit)
return CURL_READFUNC_ABORT;
// do stuff with the curl data
return tSize;
}
}
CurlCallbackHandler *m_curlHandler;
//Create an instance of above class in my dll constructor
MyDll:MyDll()
{
m_curlHandler = new CurlCallbackHandler();
}
//Cleanup above class in my dll destructor
MyDll:~MyDll()
{
delete m_curlHandler;
m_curlHandler = nullptr;
}
//Function to start receiving data asynchronously
void MyDll::GetDataAsync()
{
std::async([=]
{
//This will receive data in a new thread and call CurlDataCallback above
//This basically calls easy_perform
CurlGetData(m_curlHandler);
}
}
//Will cause the curl callback to return CURL_READFUNC_ABORT
void MyDll::StopDataAsync()
{
m_curlHandler->m_exit = true;
}
从我的主程序调用函数GetDataAsync,它基本上调用curl_easy_perform并使用m_curlHandler作为其回调函数,该函数调用回CurlDataCallback。
这一切都很好但是每当我的主程序退出时,它会调用MyDll :: StopDataAsync来停止curl数据回调,然后调用MyDll的析构函数来清理m_curlHandler。
但是我发现在那一刻curl还没有完成这个回调,程序崩溃了,因为m_curlHandler已被删除,但新的异步线程中的curl回调仍在使用它。
有时它会很好地关闭,但有时它会崩溃,因为curlcallback试图访问已经被析构函数删除的指针。
如何最好地清理m_curlHandler?我想避免等待超时,因为这会影响我的主程序的性能。
答案 0 :(得分:0)
根据C ++标准,MyDll::GetDataAsync()
函数不应该立即返回,它应该阻塞直到异步线程完成,这将有效地使操作同步。但是我相信微软有意违反了std::async
规范的这一部分,所以实际上它会立即返回,并且你可以在异步线程仍在使用时销毁回调(这正是问题所在)如果Microsoft实现遵循标准,则避免使用!)
解决方案是保持std::future
返回std::async
,然后阻止该未来(确保异步线程已完成),然后再销毁回调。
class MyDLL
{
std::future<void> m_future;
...
};
MyDll:~MyDll()
{
StopDataAsync();
m_future.get(); // wait for async thread to exit.
delete m_curlHandler; // now it's safe to do this
}
//Function to start receiving data asynchronously
void MyDll::GetDataAsync()
{
m_future = std::async([=]
{
//This will receive data in a new thread and call CurlDataCallback above
//This basically calls easy_perform
CurlGetData(m_curlHandler);
}
}
N.B。您的m_exit
成员应该是std::atomic<bool>
(或者您应该使用互斥锁来保护对它的所有读写操作),否则您的程序会出现数据争用,因此会有未定义的行为。
我还会将std::unique_ptr<CurlCallbackHandler>
用于m_curlHandler
。
我想避免等待超时,因为这会影响我的主程序的性能。
上面的解决方案将导致析构函数等待,但只有在回调需要注意m_exit == true
并导致异步线程停止运行时才会等待。这意味着你只需要等待所需的时间,而不是等待超时,这意味着猜测“足够长”的时间长度,然后可能会增加一些安全性。