為什么要使用鎖、信號量、條件變量?
網(wǎng)站不可能是單線程的,否則網(wǎng)站的性能和響應(yīng)都會(huì)收到嚴(yán)重的影響。因此,這個(gè)項(xiàng)目一定是運(yùn)行在多線程條件下的。而在多線程條件下,對共享資源的互斥訪問就極其重要。
為什么要將資源封裝成類?
首先,我們要明確資源的使用一般有三個(gè)步驟:
1、獲取資源
2、使用資源
3、釋放資源
然而,大多數(shù)情況下,我們一定能做到前兩點(diǎn),而總是忘記第三步,這就會(huì)造成資源的泄露。為了解決這個(gè)問題,提出了RAII方案,中文翻譯是資源獲取即初始化,也就是使用局部對象來管理資源的技術(shù)。
最直觀的就是將資源封裝成類, 在構(gòu)造函數(shù)中獲取資源,在析構(gòu)函數(shù)中釋放資源,這樣當(dāng)變量離開了作用域后,編譯器會(huì)調(diào)用析構(gòu)函數(shù),而析構(gòu)函數(shù)會(huì)幫助我們將資源釋放掉,這樣就避免了我們忘記釋放資源的情況。
本項(xiàng)目中,作者就將鎖、互斥量、條件變量進(jìn)行了封裝。有以下好處:
自動(dòng)資源管理:RAII模式允許資源的獲取和釋放與對象的生命周期綁定在一起。當(dāng)對象被創(chuàng)建時(shí),資源被獲??;當(dāng)對象被銷毀時(shí),資源被釋放。這消除了手動(dòng)管理資源的需要,減少了錯(cuò)誤的機(jī)會(huì)。
異常安全性:RAII模式提供了異常安全性,因?yàn)橘Y源的釋放操作通常在對象的析構(gòu)函數(shù)中執(zhí)行。如果在使用資源的過程中發(fā)生異常,對象的析構(gòu)函數(shù)會(huì)被自動(dòng)調(diào)用,確保資源被正確釋放,防止資源泄漏。
避免忘記釋放資源:使用RAII模式,開發(fā)人員不需要顯式地記住在何處釋放資源。資源的釋放是自動(dòng)的,因此避免了因忘記釋放資源而引發(fā)的問題。
代碼可讀性:RAII模式使代碼更加清晰和容易理解。資源的獲取和釋放操作都在對象的構(gòu)造函數(shù)和析構(gòu)函數(shù)中,使得代碼更加自文檔化。
資源的精確生命周期控制:RAII模式允許在對象的生命周期內(nèi)對資源的生命周期進(jìn)行精確的控制。資源在對象的構(gòu)造和析構(gòu)之間一直存在,不會(huì)在對象的其他方法之外被訪問。
并發(fā)安全性:RAII模式可以用于管理鎖、信號量等同步資源,確保線程安全性。資源在鎖住和釋放鎖時(shí)被獲取和釋放,從而避免了競態(tài)條件和死鎖等問題。
?互斥鎖、條件變量、信號量是如何工作的?
互斥鎖:提供互斥訪問的能力來確保同一時(shí)間只有一個(gè)線程能夠訪問共享資源。
? ? ? ? 1. 鎖初始化:創(chuàng)建鎖并進(jìn)行初始化;
? ? ? ? 2. 加鎖:當(dāng)線程需要訪問共享資源時(shí),嘗試獲取互斥鎖,如果已經(jīng)被別的線程占用,那么該線程會(huì)被阻塞,直到鎖可用;
? ? ? ? 3. 訪問共享資源:獲取鎖后,線程訪問共享資源;
? ? ? ? 4. 解鎖:該線程訪問完共享資源后,釋放互斥鎖,使其他等待的線程可以獲得鎖并訪問資源。
條件變量:通常和互斥鎖一起使用,條件變量用于等待某個(gè)條件滿足后再繼續(xù)執(zhí)行。
? ? ? ? 1. 等待條件:線程獲取互斥鎖后,發(fā)現(xiàn)條件不足,進(jìn)入阻塞狀態(tài),釋放互斥鎖,讓其他進(jìn)程可以訪問共享資源;
? ? ? ? 2. 喚醒等待進(jìn)程:當(dāng)條件滿足后,某個(gè)線程通過條件變量發(fā)出信號,喚醒一個(gè)或多個(gè)等待的線程,這些線程被喚醒后后會(huì)嘗試重新獲取互斥鎖。
信號量:用于控制多個(gè)線程對有限資源的訪問。通常有兩種:
? ? ? ? 1.二進(jìn)制信號量:非0即1,用于互斥訪問共享資源。等待信號量為1時(shí)獲得訪問權(quán),將信號量設(shè)置為0,訪問結(jié)束后將信號量值設(shè)為1;
? ? ? ? 2. 計(jì)數(shù)信號量:可以具有>1的值,控制多個(gè)線程對一組有限資源的訪問。線程獲得資源信號量值變小;釋放資源信號量值變大,當(dāng)信號量值為0時(shí)需要阻塞線程。
下面我們來看作者的源代碼:文章來源:http://www.zghlxwxcb.cn/news/detail-729122.html
#ifndef LOCKER_H
#define LOCKER_H
#include <exception>
#include <pthread.h>
#include <semaphore.h>
class sem
{
public:
sem()
{
if (sem_init(&m_sem, 0, 0) != 0)
{
throw std::exception();
}
}
sem(int num)
{
if (sem_init(&m_sem, 0, num) != 0)
{
throw std::exception();
}
}
~sem()
{
sem_destroy(&m_sem);
}
bool wait()
{
return sem_wait(&m_sem) == 0;
}
bool post()
{
return sem_post(&m_sem) == 0;
}
private:
sem_t m_sem;
};
class locker
{
public:
locker()
{
if (pthread_mutex_init(&m_mutex, NULL) != 0)
{
throw std::exception();
}
}
~locker()
{
pthread_mutex_destroy(&m_mutex);
}
bool lock()
{
return pthread_mutex_lock(&m_mutex) == 0;
}
bool unlock()
{
return pthread_mutex_unlock(&m_mutex) == 0;
}
pthread_mutex_t *get()
{
return &m_mutex;
}
private:
pthread_mutex_t m_mutex;
};
class cond
{
public:
cond()
{
if (pthread_cond_init(&m_cond, NULL) != 0)
{
//pthread_mutex_destroy(&m_mutex);
throw std::exception();
}
}
~cond()
{
pthread_cond_destroy(&m_cond);
}
bool wait(pthread_mutex_t *m_mutex)
{
int ret = 0;
//pthread_mutex_lock(&m_mutex);
ret = pthread_cond_wait(&m_cond, m_mutex);
//pthread_mutex_unlock(&m_mutex);
return ret == 0;
}
bool timewait(pthread_mutex_t *m_mutex, struct timespec t)
{
int ret = 0;
//pthread_mutex_lock(&m_mutex);
ret = pthread_cond_timedwait(&m_cond, m_mutex, &t);
//pthread_mutex_unlock(&m_mutex);
return ret == 0;
}
bool signal()
{
return pthread_cond_signal(&m_cond) == 0;
}
bool broadcast()
{
return pthread_cond_broadcast(&m_cond) == 0;
}
private:
//static pthread_mutex_t m_mutex;
pthread_cond_t m_cond;
};
#endif
互斥鎖和信號量是簡單的封裝,而對于條件變量作者注釋掉了互斥鎖,但并不會(huì)影響互斥訪問,因?yàn)樽髡哌x擇用封裝好的互斥鎖并且在外部來使用。例如在block_queue.h這個(gè)文件中,作者將locker m_mutex;作為私有數(shù)據(jù)成員,并通過get函數(shù)獲得該鎖。文章來源地址http://www.zghlxwxcb.cn/news/detail-729122.html
template <class T>
class block_queue
{
public:
...
if (!m_cond.timewait(m_mutex.get(), t))
{
m_mutex.unlock();
return false;
}
...
private:
locker m_mutex;
...
}
到了這里,關(guān)于TinyWebServer學(xué)習(xí)筆記-互斥鎖、信號量、條件變量的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!