我创建了一个
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
仍然不是线程安全的。
您可以在
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
而不必跳这个舞蹈...