多執行緒的libcurl的使用

大米粥的部落格發表於2022-01-17

摘要: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;
}
                   

 

相關文章