摘要:libcurl在多執行緒中,採用https訪問,經常執行一段時間,會出現crash。
libcurl的在多執行緒中的使用特別注意的有兩點:
1. curl的控制程式碼不能多執行緒共享。
2. ssl訪問時, openssl是執行緒不安全的。
知道了這兩點,就能解決libcurl無故crash的問題了。
第一點:每個執行緒初始化一個控制程式碼,供這個執行緒使用。
第二點:需要新增回撥函式,進行執行緒鎖。
參考程式碼如下:
#include <stdio.h> #include <pthread.h> #include <curl/curl.h> #define NUMT 4 /* we have this global to let the callback get easy access to it */ static pthread_mutex_t *lockarray; #ifdef USE_OPENSSL #include <openssl/crypto.h> static void lock_callback(int mode, int type, char *file, int line) { (void)file; (void)line; if(mode & CRYPTO_LOCK) { pthread_mutex_lock(&(lockarray[type])); } else { pthread_mutex_unlock(&(lockarray[type])); } } static unsigned long thread_id(void) { unsigned long ret; ret = (unsigned long)pthread_self(); return ret; } static void init_locks(void) { int i; printf("crypto num is %d\r\n", CRYPTO_num_locks()); lockarray = (pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); for(i = 0; i<CRYPTO_num_locks(); i++) { pthread_mutex_init(&(lockarray[i]), NULL); } CRYPTO_set_id_callback((unsigned long (*)())thread_id); CRYPTO_set_locking_callback((void (*)())lock_callback); } static void kill_locks(void) { int i; CRYPTO_set_locking_callback(NULL); for(i = 0; i<CRYPTO_num_locks(); i++) pthread_mutex_destroy(&(lockarray[i])); OPENSSL_free(lockarray); } /* List of URLs to fetch.*/ const char * const urls[]= { "https://www.example.com/", "https://www2.example.com/", "https://www3.example.com/", "https://www4.example.com/", }; static void *pull_one_url(void *url) { CURL *curl; curl = curl_easy_init();//每個執行緒初始化一個控制程式碼,絕對不能混用。 curl_easy_setopt(curl, CURLOPT_URL, url); /* this example doesn't verify the server's certificate, which means we might be downloading stuff from an impostor */ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_perform(curl); /* ignores error */ curl_easy_cleanup(curl); return NULL; } int main(int argc, char **argv) { pthread_t tid[NUMT]; int i; (void)argc; /* we don't use any arguments in this example */ (void)argv; /* Must initialize libcurl before any threads are started */ curl_global_init(CURL_GLOBAL_ALL); init_locks();//初始化openssl的回撥函式,載入執行緒鎖 for(i = 0; i< NUMT; i++) { int error = pthread_create(&tid[i], NULL, /* default attributes please */ pull_one_url, (void *)urls[i]); if(0 != error) fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error); else fprintf(stderr, "Thread %d, gets %s\n", i, urls[i]); } /* now wait for all threads to terminate */ for(i = 0; i< NUMT; i++) { pthread_join(tid[i], NULL); fprintf(stderr, "Thread %d terminated\n", i); } kill_locks();//銷燬openssl執行緒鎖 return 0; }