# 9.4 鎖和線程的設置
編寫多線程程序的時候,在多個線程中同時訪問同樣的數據并不總是安全的。
libevent 的結構體在多線程下通常有三種工作方式:
* 某些結構體內在地是單線程的:同時在多個線程中使用它們總是不安全的。
* 某些結構體具有可選的鎖:可以告知 libevent 是否需要在多個線程中使用每個對象。
* 某些結構體總是鎖定的 :如果 libevent 在支持鎖的配置下運行 ,在多個線程中使用它們 總是安全的。
為獲取鎖,在調用分配需要在多個線程間共享的結構體的 libevent 函數之前,必須告 知 libevent 使用哪個鎖函數。
如果使用 pthreads 庫,或者使用 Windows 本地線程代碼,那么你是幸運的:已經有設 置 libevent 使用正確的 pthreads 或者 Windows 函數的預定義函數。
### 接口
```cpp
#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
```
這些函數在成功時都返回 0,失敗時返回 -1。
如果使用不同的線程庫,則需要一些額外的工作,必須使用你的線程庫來定義函數去實現:
* 鎖
* 鎖定
* 解鎖
* 分配鎖
* 析構鎖
* 條件變量
* 創建條件變量
* 析構條件變量
* 等待條件變量
* 觸發/廣播某條件變量
* 線程
* 線程ID檢測
使用 evthread_set_lock_callbacks 和 evthread_set_id_callback 接口告知 libevent 這些函數。
### 接口
```cpp
#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 *);
```
evthread_lock_callbacks 結構體描述的鎖回調函數及其能力。對于上述版本,
lock_api_version 字段必須設置為 EVTHREAD_LOCK_API_VERSION 。必須設置 supported_locktypes 字段為 EVTHREAD_LOCKTYPE_* 常量的組合以描述支持的鎖類型 (在 2.0.4-alpha 版本中),
EVTHREAD_LOCK_RECURSIVE 是必須的,
EVTHREAD_LOCK_READWRITE 則沒有使用)。
alloc 函數必須返回指定類型的新鎖 ;
free 函數必須釋放指定類型鎖持有的所有資源 ;
lock 函數必須試圖以指定模式請求鎖定 ,如果成 功則返回0,失敗則返回非零;
unlock 函數必須試圖解鎖,成功則返回 0,否則返回非零。
### 可識別的鎖類型有:
* 0:通常的,不必遞歸的鎖。
* EVTHREAD_LOCKTYPE_RECURSIVE :不會阻塞已經持有它的線程的鎖 。一旦持有它的線程進行原來鎖定次數的解鎖,其他線程立刻就可以請求它了。
* EVTHREAD_LOCKTYPE_READWRITE :可以讓多個線程同時因為讀而持有它 ,但是 任何時刻只有一個線程因為寫而持有它。寫操作排斥所有讀操作。
### 可識別的鎖模式有:
* EVTHREAD_READ :僅用于讀寫鎖:為讀操作請求或者釋放鎖
* EVTHREAD_WRITE :僅用于讀寫鎖:為寫操作請求或者釋放鎖
* EVTHREAD_TRY :僅用于鎖定:僅在可以立刻鎖定的時候才請求鎖定
id_fn 參數必須是一個函數,它返回一個無符號長整數,標識調用此函數的線程。對于相同 線程,這個函數應該總是返回同樣的值 ;而對于同時調用該函數的不同線程 ,必須返回不同 的值。
vthread_condition_callbacks 結構體描述了與條件變量相關的回調函數。對于上述版本 , condition_api_version 字段必須設置為 EVTHREAD_CONDITION_API_VERSION 。 alloc_condition 函數必須返回到新條件變量的指針 。它接受0作為其參數。free_condition 函 數必須釋放條件變量持有的存儲器和資源。 wait_condition 函數要求三個參數:一個 由 alloc_condition 分配的條件變量 ,一個由你提供的 evthread_lock_callbacks.alloc 函數分配 的鎖,以及一個可選的超時值 。調用本函數時 ,必須已經持有參數指定的鎖 ;本函數應該釋 放指定的鎖,等待條件變量成為授信狀態,或者直到指定的超時時間已經流逝(可選 )。 wait_condition 應該在錯誤時返回-1,條件變量授信時返回0,超時時返回1。返回之前,函 數應該確定其再次持有鎖。最后, signal_condition 函數應該喚醒等待該條件變量的某個線 程(broadcast 參數為 false 時),或者喚醒等待條件變量的所有線程(broadcast 參數為 true 時)。只有在持有與條件變量相關的鎖的時候,才能夠進行這些操作。
>關于條件變量的更多信息,請查看 pthreads 的 pthread_cond_*函數文檔,或者 Windows 的 CONDITION_VARIABLE(Windows Vista 新引入的)函數文檔。
### 實例:
```cpp
關于使用這些函數的示例,
請查看 Libevent 源代碼發布版本中的
evthread_pthread.c 和 evthread_win32.c 文件。
```
這些函數在 <event2/thread.h> 中聲明,其中大多數在 2.0.4-alpha 版本中首次出現。 2.0.1-alpha 到2.0.3-alpha 使用較老版本的鎖函數 。event_use_pthreads 函數要求程序鏈接 到 event_pthreads 庫。
條件變量函數是2.0.7-rc 版本新引入的,用于解決某些棘手的死鎖問題。
可以創建禁止鎖支持的libevent。這時候已創建的使用上述線程相關函數的程序將不能運行。
## 調試做的使用
為幫助調試鎖的使用,libevent 有一個可選的“鎖調試”特征。這個特征包裝了鎖調用,以便捕獲典型的鎖錯誤,包括:
* 解鎖并沒有持有的鎖
* 重新鎖定一個非遞歸鎖
如果發生這些錯誤中的某一個, libevent 將給出斷言失敗并且退出。
```cpp
void event_enable_debug_mode(void);
```
必須在創建或者使用任何鎖之前調用這個函數。為安全起見,請在設置完線程函數后立即調用這個函數。
- 封面
- 1 Libevent官方
- 2 epoll
- 2.1 流-IO操作-阻塞
- 2.2 解決阻塞死等待的辦法
- 2.3 什么是epoll
- 2.4 epollAPI
- 2.5 觸發模式
- 2.6 簡單的epoll服務器
- 3 epoll和reactor
- 3.1 reactor反應堆模式
- 3.2 epoll的反應堆模式實現
- 4 event_base
- 4.1 創建event_base
- 4.2 檢查event_base后端
- 4.3 釋放event_base
- 4.4 event_base優先級
- 4.5 event_base和fork
- 5 事件循環event_loop
- 5.1 運行循環
- 5.2 停止循環
- 5.3 轉儲event_base的狀態
- 6 事件event
- 6.1 創建事件
- 6.2 事件的未決和非未決
- 6.3 事件的優先級
- 6.4 檢查事件狀態
- 6.5 一次觸發事件
- 6.6 手動激活事件
- 6.7 事件狀態之間的轉換
- 7 數據緩沖Bufferevent
- 7.1 回調和水位
- 7.2 延遲回調
- 7.3 bufferevent 選項標志
- 7.4 使用bufferevent
- 7.5 通用bufferevent操作
- 7.5.1 釋放bufferevent操作
- 7.5.2 操作回調、水位和啟用/禁用
- 7.5.3 操作bufferevent中的數據
- 7.5.4 bufferevent的清空操作
- 8 數據封裝evBuffer
- 8.1 創建和釋放evbuffer
- 8.2 evbuffer與線程安全
- 8.3 檢查evbuffer
- 8.4 向evbuffer添加數據
- 8.5 evbuffer數據移動
- 8.6 添加數據到evbuffer前
- 8 鏈接監聽器evconnlistener
- 8.1 創建和釋放 evconnlistener
- 8.2 啟用和禁用 evconnlistener
- 8.3 調整 evconnlistener 的回調函數
- 8.4 檢測 evconnlistener
- 8.5 偵測錯誤
- 9 libevent常用設置
- 9.1 日志消息回調設置
- 9.2 致命錯誤回調設置
- 9.3 內存管理回調設置
- 9.4 鎖和線程的設置
- 9.5 調試事件的使用
- 10 基于libevent服務器
- 10.1 Hello_World服務器(基于信號)
- 10.2 基于事件服務器
- 10.3 回顯服務器
- 10.3 libevent實現http服務器