1
  1. 我有一个 MainProgram.exe 调用 MyDll.dll 并使用 curl 在回调函数中接收数据。
  2. 我将 curl 包装在一个名为 CurlGetData 的函数中,该函数创建一个 curl 实例并执行 curl_easy_perform。

这是我的代码:

//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?我想避免等待超时,因为这会影响我的主程序的性能。

4

1 回答 1

0

根据 C++ 标准,MyDll::GetDataAsync()函数不应该立即返回,它应该阻塞直到异步线程完成,这将有效地使操作同步。但是我相信微软故意违反了std::async规范的这一部分,所以实际上它确实立即返回,并且你有可能在异步线程仍在使用它时销毁回调(这正是微软实现时可以避免的问题符合标准!)

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

注意您的m_exit成员应该是std::atomic<bool>(或者您应该使用互斥锁来保护对它的所有读取和写入),否则您的程序会出现数据竞争,因此会有未定义的行为。

我也会使用std::unique_ptr<CurlCallbackHandler>for m_curlHandler

我想避免等待超时,因为这会影响我的主程序的性能。

上面的解决方案导致您的析构函数等待,但只要回调注意到这一点m_exit == true并导致异步线程停止运行。这意味着您只等待必要的时间,不再等待,与超时不同,超时意味着猜测“足够长”的时间,然后可能会添加更多以确保安全。

于 2015-03-10T23:20:51.230 回答