《linux高性能服務器編程》—— 三種線程同步機制的包裝類:POSIX信號量、互斥鎖和條件變量

(1)POSIX信號量:提供對共享資源的獨佔式訪問

以下使用未命名信號量作爲包裝類的核心。

class sem
{
private:
    sem_t semp; //信號量
public:
    sem()
    {
        //初始化一個信號量,第2個參數=0則表示此信號量爲本進程所有,否則可在多個進程間共享
        //第三個參數指定信號量初始值
        if(sem_init(&semp, 0, 1) != 0)
        {
            throw std::exception();
        }
    }
    ~sem()
    {
        //銷燬信號量,釋放其佔用的資源;若銷燬一個正在被其他線程等待的信號量,則會導致不可預期的後果
        sem_destroy(&semp);
    }
    bool wait()
    {
        //以原子操作的方式:判斷信號量的值,若爲0,則被阻塞,直到值大於0;否則,使信號量的值-1
        return sem_wait(&semp) == 0;
    }
    bool post()
    {
        //以原子操作的方式:使信號量的值+1
        return sem_post(&semp) == 0;
    }
};

(2)互斥鎖:防止多線程同時訪問共享資源

class locker
{
private:
    pthread_mutex_t mutex; //互斥鎖
public:
    locker()
    {
        //初始化互斥鎖,第二個參數指定互斥鎖屬性
        if(pthread_mutex_init(&mutex, NULL) != 0)
        {
            throw std::exception();
        }
    }
    ~locker()
    {
        //銷燬互斥鎖,釋放其佔用的資源;若此互斥鎖處於被加鎖的狀態,則銷燬它會導致不可預期的後果
        pthread_mutex_destroy(&mutex);
    }
    bool lock()
    {
        //以原子操作的方式:判斷互斥鎖的狀態,若已被鎖上,則阻塞,直到被解鎖;否則,加鎖
        return pthread_mutex_lock(&mutex) == 0;
    }
    bool unlock()
    {
        //以原子操作的方式給互斥鎖解鎖
        return pthread_mutex_unlock(&mutex) == 0;
    }
};

---- 發生死鎖的常見情況?

  • 當線程想要申請某資源時,此資源已被本線程所加鎖,因而線程被阻塞,然而此資源能被解鎖的前提是本線程將此資源解鎖,但線程已被阻塞,於是陷入死鎖;
  • 當線程想要申請某資源時,此資源已被其他線程所加鎖,因而線程被阻塞,然而此資源能被解鎖的前提是本線程將另一資源解鎖,但線程已被阻塞,於是陷入死鎖。

很顯然,線程發生死鎖的本質在於線程在申請某資源時陷入阻塞,但唯有繼續運行才能使此資源被解鎖,陷入矛盾。

---- 如何避免死鎖?

先大致判斷是否可能發生死鎖,若可能則使用非阻塞的加鎖接口。

當線程無法獲取鎖(即非阻塞的加鎖函數返回失敗)時,可以嘗試着釋放掉已佔有的鎖,過一段時間後再嘗試加鎖。

(3)條件變量:允許多個線程以無競爭的方式等待特定的條件發生

class condvar
{
private:
    pthread_mutex_t mutex; //互斥鎖
    pthread_cond_t cond; //條件變量
public:
    condvar()
    {
        //初始化互斥鎖
        if(pthread_mutex_init(&mutex, NULL) != 0)
        {
            throw std::exception();
        }
        //初始化條件變量,第二個參數指定條件變量的屬性
        if(pthread_cond_init(&m_cond, NULL) != 0)
        {
            //若構造失敗,則釋放已分配的資源
            pthread_mutex_destroy(&mutex);
            throw std::exception();
        }
    }
    ~condvar()
    {
        //銷燬互斥鎖和條件變量
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&m_cond);
    }
    bool wait()
    {
        //先給互斥鎖加鎖
        pthread_mutex_lock(&mutex);

        //pthread_cond_wait將調用線程放入條件變量的等待序列中,然後將互斥鎖解鎖
        //這保證了從pthread_cond_wait開始執行到調用線程被放入條件變量的等待隊列的這段時間內,
        //條件變量不會被修改
        //這避免錯過條件變量的變化
        //pthread_cond_wait成功返回後互斥鎖又被鎖上
        int ret = pthread_cond_wait(&m_cond, &mutex);
        
        //最後給互斥鎖解鎖
        pthread_mutex_unlock(&mutex);
        return ret == 0;
    }
    bool signal()
    {
        //喚醒一個等待目標條件變量的線程
        return pthread_cond_signal(&m_cond) == 0;
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章