muduo網路庫學習筆記(4):互斥量和條件變數
事實上,muduo中對互斥量和條件變數的操作基本上都是呼叫它們對應的相關函式來實現的。例如MutexLock::lock()即呼叫pthread_mutex_lock(),Condition::wait()即呼叫pthread_cond_wait()等等。
互斥量
muduo封裝了MutexLock和MutexLockGuard。MutexLock封裝臨界區,它是一個簡單的資源類,用RAII手法封裝互斥量的建立與銷燬,MutexLock一般是別的類的資料成員。MutexLockGuard封裝臨界區的進入和退出,即加鎖和解鎖,它一般是個棧上物件,作用域剛好等於臨界區域。
類圖:
(1)需要理解的是使用RAII(資源獲取就是初始化)手法封裝MutexLockGuard:不手工呼叫lock()和unlock()函式,一切交給棧上的Guard物件的構造和解構函式負責。
(2)互斥鎖通常用於保護由多個執行緒或多程式分享的共享資料,一般是一些可供執行緒間使用的全域性變數,來達到執行緒同步的目的,即保證任何時刻只有一個執行緒或程式在執行其中的程式碼。一般加鎖的輪廓如下:
pthread_mutex_lock()
臨界區
pthread_mutex_unlock()
(3)互斥量相關函式
互斥鎖的初始化有兩種初始化方式:
I.對於靜態分配的互斥鎖一般用巨集賦值的方式初始化,
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
II.對於動態分配的互斥鎖(如呼叫malloc)或分配在共享記憶體中,則必須呼叫pthread_mutex_init()函式來進行初始化。
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); // 引數mutexattr指定互斥量的屬性,NULL為預設屬性
// pthread_mutex_lock和pthread_mutex_unlock都是原子操作
// 如果一個執行緒呼叫pthread_mutex_lock試圖鎖住互斥量,而該互斥量,又被其他執行緒鎖住(佔用)
// 則該執行緒的pthread_mutex_lock呼叫就會阻塞直到其他執行緒對該互斥量進行解鎖,該執行緒才能獲得該互斥量
// pthread_mutex_lock呼叫才會返回
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
返回值:成功時返回0,失敗時返回錯誤程式碼,它們並不設定errno
(4)作者在Mutex.h程式碼的最後一行還定義了一個巨集:
// Prevent misuse like:
// MutexLockGuard(mutex_);
// A tempory object doesn't hold the lock for long!
#define MutexLockGuard(x) error "Missing guard object name"
它的作用是防止程式裡出現如下錯誤:
void doit()
{
MutexLockGuard(mutex); // 遺漏了變數名,產生一個臨時物件又馬上銷燬了
// 結果沒有鎖住臨界區
// 正確寫法是 MutexLockGuard lock(mutex);
}
條件變數
如果需要等待某個條件成立,我們應該使用條件變數,條件變數顧名思義是一個或多個執行緒等待某個布林表示式為真,即等待別的執行緒“喚醒”它,條件變數的使用是與互斥鎖共同使用的。
muduo Condition類圖:
(1)條件變數相關函式
#include <pthread.h>
// pthread_cond_t型別的變數可以用PTHREAD_COND_INITIALIZER常量進行靜態初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
// 初始化。當cond_attr為NULL時,使用預設的屬性
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
// 喚醒等待在相應條件變數上的一個執行緒
int pthread_cond_signal(pthread_cond_t *cond);
// 喚醒阻塞在相應條件變數上的所有執行緒
int pthread_cond_broadcast(pthread_cond_t *cond);
// 會自動解鎖互斥量,等待條件變數被觸發
// 在呼叫pthread_cond_wait之前,應用程式必須加鎖互斥量
// pthread_cond_wait函式返回前,自動重新對互斥量加鎖
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
// 允許執行緒就阻塞時間設定一個限制值
// 如果在abstime指定的時間內cond未觸發,互斥量mutex被重新加鎖且pthread_cond_timedwait返回錯誤ETIMEDOUT
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
// 銷燬一個條件變數,釋放它擁有的資源
int pthread_cond_destroy(pthread_cond_t *cond);
(2)需要注意的是:如果一個class要包含MutexLock和Condition,要注意它們的宣告順序和初始化順序(初始化順序要與成員宣告保持一致),mutex_應先於condition_構造,並作為後者的構造引數。
(3)clock_gettime函式和struct timespec結構
Contidion::waitForSeconds()用到了clock_gettime函式設定限制時間。
#include <time.h>
// 最高精度為納秒
struct timespec {
time_t tv_sec; // seconds
long tv_nsec; // nanoseconds
};
// clock_gettime函式原型
// 可以用於計算精度和納秒
long sys_clock_gettime (clockid_t which_clock, struct timespec *tp);
which_clock引數解釋:
1.CLOCK_REALTIME:系統實時時間,隨系統實時時間改變而改變,即從UTC1970-1-1 00:00:00開始計時,中間時刻如果系統時間被使用者該成其他,則對應的時間相應改變
2.CLOCK_MONOTONIC:從系統啟動這一刻起開始計時,不受系統時間被使用者改變的影響
3.CLOCK_PROCESS_CPUTIME_ID:本程式到當前程式碼系統CPU花費的時間
4.CLOCK_THREAD_CPUTIME_ID:本執行緒到當前程式碼系統CPU花費的時間
利用條件變數實現的倒數計時門閂類
類圖:
倒數計時是一種常用且易用的同步手段,它主要有兩種用途:
(1)主執行緒發起多個子執行緒,等這些子執行緒各自都完成一定的任務之後,主執行緒才繼續執行。通常用於主執行緒等待多個子執行緒完成初始化。
(2)主執行緒發起多個子執行緒,子執行緒都等待主執行緒,主執行緒完成其他一些任務之後通知所有子執行緒開始執行。通常用於多個子執行緒等待主執行緒發出“起跑”命令。
CountDownLatch的介面和實現很簡單:
檔名:CountDownLatch.h
class CountDownLatch : boost::noncopyable
{
public:
explicit CountDownLatch(int count); // count表示倒數幾次
void wait(); // 等待計數值變為0
void countDown(); // 計數減一
int getCount() const;
private:
mutable MutexLock mutex_;
Condition condition_;
int count_;
};
檔名:CountDownLatch.cc
void CountDownLatch::wait()
{
MutexLockGuard lock(mutex_);
while(count_ > 0)
condition_.wait();
}
void CountDownLatch::countDown()
{
MutexLockGuard lock(mutex_);
--count_;
if(count_ == 0)
conditon_.notifyAll();
}
相關文章
- 互斥鎖和條件變數 (轉)變數
- muduo網路庫學習筆記(1):Timestamp類筆記
- muduo網路庫學習筆記(2):原子性操作筆記
- muduo網路庫學習筆記(3):Thread類筆記thread
- muduo網路庫學習筆記(12):TcpServer和TcpConnection類筆記TCPServer
- muduo網路庫學習筆記(14):chargen服務示例筆記
- muduo網路庫學習筆記(11):有用的runInLoop()函式筆記OOP函式
- 互斥鎖與條件變數學習與應用小結變數
- muduo網路庫學習筆記(13):TcpConnection生命期的管理筆記TCP
- muduo網路庫學習筆記(10):定時器的實現筆記定時器
- muduo網路庫學習筆記(7):執行緒特定資料筆記執行緒
- muduo網路庫學習筆記(9):Reactor模式的關鍵結構筆記React模式
- muduo網路庫學習筆記(8):高效日誌類的封裝筆記封裝
- muduo網路庫學習筆記(5):執行緒池的實現筆記執行緒
- muduo網路庫學習筆記(15):關於使用stdio和iostream的討論筆記iOS
- muduo網路庫學習筆記(6):單例類(執行緒安全的)筆記單例執行緒
- muduo網路庫學習之muduo_http 庫涉及到的類HTTP
- Linux Qt使用POSIX多執行緒條件變數、互斥鎖(量)LinuxQT執行緒變數
- React學習筆記-條件渲染React筆記
- muduo網路庫學習之muduo_inspect 庫涉及到的類
- 附個人工作程式碼 條件變數深度運用、互斥鎖+訊號量變數
- linux多執行緒-----同步機制(互斥量、讀寫鎖、條件變數)Linux執行緒變數
- MySQL 變數和條件MySql變數
- muduo網路庫學習之EventLoop(七):TcpClient、ConnectorOOPTCPclient
- Vue學習筆記(三)條件渲染和迴圈渲染Vue筆記
- 學習《PLSQL開發指南》筆記——條件和序列控制SQL筆記
- MySQL學習筆記之約束條件MySql筆記
- RT-Thread學習筆記2-互斥量與訊號量thread筆記
- 透過互斥鎖+條件量的方式實現同步與互斥
- 【OCP學習筆記】配置網路環境 -- 4筆記
- linux多執行緒-----同步物件(互斥量、讀寫鎖、條件變數)的屬性Linux執行緒物件變數
- mysql,where條件查詢等學習筆記MySql筆記
- muduo網路庫學習之EventLoop(一):事件迴圈類圖簡介和muduo 定時器TimeQueueOOP事件定時器
- muduo網路庫AtomicIntegerT原子整數類
- Python學習筆記 - 變數Python筆記變數
- javascript學習筆記,二、變數JavaScript筆記變數
- Tensorflow學習筆記: 變數及共享變數筆記變數
- linux中條件變數和訊號量的區別!Linux變數