參考libevent官方提供的文檔: http://www.wangafu.net/~nickm/libevent-book/Ref1_libsetup.html
這一篇主要翻譯libevent多線程的使用接口和文檔。
As you probably know if you’re writing multithreaded programs, it isn’t always safe to access the same data from multiple threads at the same time.
Libevent structures can generally work three ways with multithreading.
-
Some structures are inherently single-threaded: it is never safe to use them from more than one thread at the same time.
-
Some structures are optionally locked: you can tell Libevent for each object whether you need to use it from multiple threads at once.
-
Some structures are always locked: if Libevent is running with lock support, then they are always safe to use from multiple threads at once.
當你編寫多線程程序的時候,多個線程訪問同一塊數據並不安全。對於多線程libevent通常采取以下三種方式工作,
1 一些結構內不是單線程的,多線程同時訪問這個結構是不安全的。
2一些結構內部是選擇性加鎖的,你需要通知libevent,對於每個對象你是否采用多線程的方式使用它。
3一些結構總是加鎖的,如果libevent設置了加鎖的模式,采用多線程方式是安全的。
To get locking in Libevent, you must tell Libevent which locking functions to use. You need to do this before you call any Libevent function
that allocates a structure that needs to be shared between threads.
If you are using the pthreads library, or the native Windows threading code, you’re in luck. There are pre-defined functions that will set
Libevent up to use the right pthreads or Windows functions for you.
如果要使用libevent多線程鎖的功能,需要開辟一個線程共享的結構,如果您使用windows或者linux提供的pthread庫,libevent已經定義好了。
#ifdef WIN32 int evthread_use_windows_threads(void); #define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED #endif #ifdef _EVENT_HAVE_PTHREADS int evthread_use_pthreads(void); #define EVTHREAD_USE_PTHREADS_IMPLEMENTED #endif
libevent針對win32平台定義了evthread_use_windows_threads,
libevent針對Linux thread庫 定義了evthread_use_pthreads
evthread_use_pthread函數是這樣的
int evthread_use_pthreads(void) { //pthread lock callback結構體對象 struct evthread_lock_callbacks cbs = { //鎖的版本 EVTHREAD_LOCK_API_VERSION, //鎖的屬性 EVTHREAD_LOCKTYPE_RECURSIVE, //創建鎖 evthread_posix_lock_alloc, //回收鎖 evthread_posix_lock_free, //加鎖回調函數 evthread_posix_lock, //解鎖回調函數 evthread_posix_unlock }; //條件變量回調結構體對象 struct evthread_condition_callbacks cond_cbs = { //條件變量的版本 EVTHREAD_CONDITION_API_VERSION, //創建條件變量 evthread_posix_cond_alloc, //回收條件變量 evthread_posix_cond_free, //激活條件的回調函數 evthread_posix_cond_signal, //條件不滿足阻塞的回調函數 evthread_posix_cond_wait }; //設置互斥鎖的屬性 /* Set ourselves up to get recursive locks. */ if (pthread_mutexattr_init(&attr_recursive)) return -1; if (pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE)) return -1; //將cbs的屬性設置到全局變量中,分為調試和正常模式 evthread_set_lock_callbacks(&cbs); //同樣將cond_cbs設置到全局變量 evthread_set_condition_callbacks(&cond_cbs); //設置線程id到全局變量 evthread_set_id_callback(evthread_posix_get_id); return 0; }
這幾個結構體如下
#define EVTHREAD_WRITE 0x04 #define EVTHREAD_READ 0x08 #define EVTHREAD_TRY 0x10 #define EVTHREAD_LOCKTYPE_RECURSIVE 1 #define EVTHREAD_LOCKTYPE_READWRITE 2 #define EVTHREAD_LOCK_API_VERSION 1 struct evthread_lock_callbacks { int lock_api_version; unsigned supported_locktypes; void *(*alloc)(unsigned locktype); void (*free)(void *lock, unsigned locktype); int (*lock)(unsigned mode, void *lock); int (*unlock)(unsigned mode, void *lock); }; int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *); void evthread_set_id_callback(unsigned long (*id_fn)(void)); struct evthread_condition_callbacks { int condition_api_version; void *(*alloc_condition)(unsigned condtype); void (*free_condition)(void *cond); int (*signal_condition)(void *cond, int broadcast); int (*wait_condition)(void *cond, void *lock, const struct timeval *timeout); }; int evthread_set_condition_callbacks( const struct evthread_condition_callbacks *);
The evthread_lock_callbacks structure describes your locking callbacks and their abilities. For the version described above, the lock_api_version field must be set to
EVTHREAD_LOCK_API_VERSION. The supported_locktypes field must be set to a bitmask of the EVTHREAD_LOCKTYPE_* constants to describe which lock types you can support.
(As of 2.0.4-alpha, EVTHREAD_LOCK_RECURSIVE
is mandatory and EVTHREAD_LOCK_READWRITE is unused.) The alloc function must return a new lock of the specified type. The free function must release all resources held by a lock of the
specified type. The lock function must try to acquire the lock in the specified mode, returning 0 on success and nonzero on failure. The unlock function must try to unlock the lock, returning 0 on success and nonzero on failure.
理解:
evthread_lock_callbacks 包括了鎖的回調函數和他們的功能,lock_api_version 要被設置為EVTHREAD_LOCK_API_VERSION,
supported_locktypes 應該設置為自己需要的EVTHREAD_LOCKTYPE_開頭的幾個類型的bitmask按位或
alloc 函數返回指定類型的鎖,free 函數釋放指定類型的鎖的所有資源,lock 函數試圖獲取制定模式的鎖,成功返回0,失敗返回非0.
unlock 解鎖函數釋放指定的鎖,成功返回0,失敗返回非0
Recognized lock types are:
- 0
-
A regular, not-necessarily recursive lock.
- EVTHREAD_LOCKTYPE_RECURSIVE
-
A lock that does not block a thread already holding it from requiring it again. Other threads can acquire the lock once the thread holding it has unlocked it as many times
as it was initially locked.
- EVTHREAD_LOCKTYPE_READWRITE
-
A lock that allows multiple threads to hold it at once for reading, but only one thread at a time to hold it for writing. A writer excludes all readers.
0表示常規鎖,不可以被重復上鎖
EVTHREAD_LOCKTYPE_RECURSIVE:這種鎖當一個線程持有,該線程可以繼續獲取他而不被阻塞,其他線程需要等到該線程釋放這個鎖后可以獲取到這個鎖,
並且可以多次加鎖。
EVTHREAD_LOCKTYPE_READWRITE:這種鎖多線程可以在讀的時候都獲取到他,但是寫操作時只允許一個線程持有。
Recognized lock modes are:
- EVTHREAD_READ
-
For READWRITE locks only: acquire or release the lock for reading.
- EVTHREAD_WRITE
-
For READWRITE locks only: acquire or release the lock for writing.
- EVTHREAD_TRY
-
For locking only: acquire the lock only if the lock can be acquired immediately.
EVTHREAD_READ和EVTHREAD_WRITE都是針對READWRITE 鎖的獲取和釋放。
EVTHREAD_TRY:這個模式只在能立即獲得鎖的時候獲取鎖,否則不等待。
The id_fn argument must be a function returning an unsigned long identifying what thread is calling the function. It must always return the same number for the same thread, and must not ever return the same number for two different threads if they are both executing at the same time.
id_fn函數返回一個unsigned long標識調用該函數的線程。不同線程的返回值不同,同一個線程的返回值相同。
The evthread_condition_callbacks structure describes callbacks related to condition variables. For the version described above, the lock_api_version field must be set to EVTHREAD_CONDITION_API_VERSION. The alloc_condition function must return a pointer to a new condition variable. It receives 0 as its argument. The free_condition function must release storage and resources held by a condition variable. The wait_condition function takes three arguments: a condition allocated by alloc_condition, a lock allocated by the evthread_lock_callbacks.alloc function you provided, and an optional timeout. The lock will be held whenever the function is called; the function must release the lock, and wait until the condition becomes signalled or until the (optional) timeout has elapsed. The wait_condition function should return -1 on an error, 0 if the condition is signalled, and 1 on a timeout. Before it returns, it should make sure it holds the lock again. Finally, the signal_condition function should cause one thread waiting on the condition to wake up (if its broadcast argument is false) and all threads currently waiting on the condition to wake up (if its broadcast argument is true). It will only be held while holding the lock associated with the condition.
evthread_condition_callbacks 描述了幾個跟條件變量相關的回調函數。lock_api_version 應該被設置為EVTHREAD_CONDITION_API_VERSION,alloc_condition 喊回一個指向新的環境變量的指針,
free_condition 釋放條件變量的資源,wait_condition 帶有三個參數,分別是alloc_condition開辟的條件變量,evthread_lock_callbacks開辟的鎖,以及一個可選的超時值,在調用這個函數時lock要提前加鎖,
之后,函數內部必須釋放鎖,等待條件被喚醒或者超時,wait_condition 在錯誤時返回-1,超時返回1,被激活返回0.在該函數內部返回之前,他要自己上鎖。signal_condition 激活單個線程,broadcast 參數設為true時
所有等待該條件的線程被激活。只有持有和條件相關的鎖的時候線程才會被掛起。
libevent中開辟鎖和釋放等等的回調函數以及條件變量的相關函數實現比較簡單,就不展開了,可以查看evthread_pthread.c文件。
下面看下系統是如何調用
evthread_set_condition_callbacks()和evthread_set_lock_callbacks()分別將條件回調的結構體對象和鎖相關功能的結構體對象
賦值給全局變量
_evthread_cond_fns和_evthread_lock_fns,
libevent封裝了幾個通過_evthread_cond_fns和 _evthread_lock_fns 調用鎖和條件變量的接口,
都在evthread-internal.h文件里。
EVTHREAD_ALLOC_LOCK(lockvar, locktype);
EVTHREAD_FREE_LOCK(lockvar, locktype);
EVLOCK_LOCK(lockvar,mode);
EVLOCK_UNLOCK(lockvar,mode);
EVTHREAD_ALLOC_COND(condvar);
EVTHREAD_FREE_COND(cond);
EVTHREAD_COND_SIGNAL(cond);
EVTHREAD_COND_WAIT(cond, lock);
等等,就不展開了,讀者自己閱讀。
我的公眾號