在多线程环境c++中使用libcurl

问题描述 投票:0回答:1

我创建了一个

APIExecutor
类,理想情况下它应该能够生成 api 请求并以线程安全的方式处理响应。由于我使用的是curl 7.29.0,因此我在
ApiExecutor
构造函数和析构函数中使用互斥锁定/解锁。

static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}

class APIExecutor
{
    string mResponse;
    CURL *mCurl;
    static std::mutex mCurlMutex;

    APIExecutor()
    {
        std::mutex mCurlMutex;
        mCurlMutex.lock();
        mCurl = curl_easy_init();
    }
    ~APIExecutor()
    {
        std::mutex mCurlMutex;
        curl_easy_cleanup(mCurl);
        mCurlMutex.unlock();
    }

    std::string InvokeAPI(std::string& rest_url)
    {
        mResponse = "";
        //setting some more opts
        curl_easy_setopt(mCurl, CURLOPT_URL, rest_url.c_str()); 
        curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, WriteCallback);       
        curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, &mResponse);
        const CURLcode rc = curl_easy_perform(mCurl);
        return mResponse;
    }
}

APIExecutor executor;
boost::property_tree::ptree mResRoot;
std::vector<std::string> vecApis1;
std::vector<std::string> vecApis2;

void ReadResponse(std::string& ss)
{
    std::stringstream sstream;
    sstream << ss;

    /* Read json. */
    try {
        boost::property_tree::read_json(sstream, mResRoot);
    }
    catch (std::exception const& e)
    {
        std::cerr << e.what() << std::endl;
        throw e.what();
    }
}
//Similarly we have testApi2() which iterates vecApis2
void testApi1()
{
    /*
    *In the actual use case, API URLs generated in each thread.
    *Using a pre-defined vecApis just for reference purpose
    */
    for(string& s : vecApis1)
    {
        string response;
        response = executor.InvokeAPI(s);
        cout << response << endl;
        
        try
        {
            ReadResponse(response);
        }
        catch (const std::exception& ex)
        {
            std::string s = "Error json parse failed :"  + std::string(ex.what());
            std::cout << __func__ << ":" << __LINE__ << " " << s << std::endl;
            throw std::runtime_error(s);
        }
        catch(...)
        {
            std::string s = "Error json parse failed ";
            std::cout << __func__ << ":" << __LINE__ << " " << s << std::endl;
            throw s.c_str();
        }   
    }
}

void popVec1()
{
    vecApis1.push_back("randomApiString1");
    vecApis1.push_back("randomApiString2");
    vecApis1.push_back("randomApiString3");
}
void popVec2()
{
    vecApis2.push_back("randomApiString4");
    vecApis2.push_back("randomApiString5");
    vecApis2.push_back("randomApiString6");
}

int main()
{
    popVec1();popVec2();
    thread t1(testApi1), t2(testApi2);
    t1.join(); t2.join();
    return 0;
}

虽然每次使用正确的 URL 时,我确实在

cout
之前正确地看到至少 2 个
curl_easy_perform
,但程序仅由于分段错误而终止。在我看来,尽管由于在构造函数中锁定了互斥锁,curl 调用的序列化正确发生,但
InvokeAPI
仍然不是线程安全的。

c++ multithreading curl libcurl
1个回答
0
投票

您可以在

executor
的构造函数和析构函数中锁定和解锁互斥体。其中只有一个实例。您希望确保同时只有一个
InvokeAPI
实例在运行,因此您需要获取
InvokeAPI
中的互斥体。删除构造函数和析构函数中的锁定和解锁调用,以及无用的
mCurlMutex
局部变量。使
mCurlMutex
非静态并更改
InvokeAPI
,如下所示:

    std::string InvokeAPI(std::string& rest_url)
    {
        std::lock_guard<std::mutex> lock{mCurlMutex};
        mResponse = "";
        //setting some more opts
        curl_easy_setopt(mCurl, CURLOPT_URL, rest_url.c_str()); 
        curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, WriteCallback);       
        curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, &mResponse);
        const CURLcode rc = curl_easy_perform(mCurl);
        return mResponse;
    }

当然,整个答案是基于您坚持使用单个 APIExecutor 来包装单个

CURL*
。你也可以在
CURL*
中创建一个本地
InvokeAPI
而不必跳这个舞蹈...

© www.soinside.com 2019 - 2024. All rights reserved.