線程的同步機(jī)制(互斥鎖,條件變量,信號量,讀寫鎖,自旋鎖)

互斥鎖

  • 初始化
#inlude <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);

attr鎖屬性非NULL時:
PTHREAD_MUTEX_TIMED_NP:普通鎖
PTHREAD_MUTEX_RECURSIVE_NP:嵌套鎖(同一鎖可多次加鎖)
PTHREAD_MUTEX_ERRORCHECK_NP:檢錯鎖
PTHREAD_MUTEX_ADAPTIVE_NP:適應(yīng)鎖,釋放后重新競爭

  • 銷毀
#inlude <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
  • 申請互斥鎖(加鎖)
#inlude <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);//沒成功(已被加鎖)就阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex);//沒成功(已被加鎖)就返回
  • 釋放互斥鎖(解鎖)
#inlude <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
示例代碼:
#include <pthread.h>
#include <unistd.h>
#include <iostream>
using namespace std;

pthread_mutex_t mutex;

int sum=0;
void *fun1(void *arg)
{
    int i=5;
    pthread_mutex_lock(&mutex);
    

    while(i>0){
        sleep(1);
        cout<<"thread1"<<endl;
        i--;
        sum++;  
    }
    pthread_mutex_unlock(&mutex);
}

void *fun2(void *arg)
{
    int i=5;
    pthread_mutex_lock(&mutex);
    

    while(i>0){
        sleep(1);
        cout<<"thread2"<<endl;
        i--;
        sum--;  
    }
    pthread_mutex_unlock(&mutex);
}

int main()
{
    pthread_t thread1,thread2;
    pthread_mutex_init(&mutex,NULL);
    pthread_create(&thread1,NULL,fun1,NULL);
    pthread_create(&thread2,NULL,fun2,NULL);

    pthread_join(thread1,NULL);
    pthread_join(thread2,NULL);
    pthread_mutex_destroy(&mutex);
    cout << "sum= "<<sum <<endl;
    return 0;
}

運(yùn)行結(jié)果:



若將加鎖解鎖注釋掉后運(yùn)行結(jié)果


條件變量

若有一個臨界資源如一個緩沖區(qū)(字符數(shù)組),當(dāng)緩沖區(qū)為空時線程A寫數(shù)據(jù),當(dāng)緩沖區(qū)有數(shù)據(jù)時B取出數(shù)據(jù)。此時光靠互斥鎖不能滿足要求,就需要Linux另一個同步機(jī)制----條件變量。

  • 初始化
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);
  • 銷毀
#include <pthread.h>
int pthread_cond_destroy(pthread_cond_t *cond);
  • 阻塞等待條件變量(p操作)
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);//在指定時間之內(nèi)等待

wait在調(diào)用的時候, 這個線程會釋放mutex, 并且給這個cond上鎖, 線程被掛起, 不占用CPU,被喚醒的時候這個搶到鎖線程會自動重新上鎖 —— 即重新獲得mutex

無論哪種等待方式,都必須和一個互斥鎖配合,以防止多個線程同時請求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的競爭條件(Race Condition)。mutex互斥鎖必須是普通鎖(PTHREAD_MUTEX_TIMED_NP)或者適應(yīng)鎖(PTHREAD_MUTEX_ADAPTIVE_NP),且在調(diào)用pthread_cond_wait()前必須由本線程加鎖(pthread_mutex_lock()),而在更新條件等待隊(duì)列以前,mutex保持鎖定狀態(tài),并在線程掛起進(jìn)入等待前解鎖。在條件滿足從而離開pthread_cond_wait()之前,mutex將被重新加鎖,以與進(jìn)入pthread_cond_wait()前的加鎖動作對應(yīng)。
————————————————
版權(quán)聲明:本文為CSDN博主「貓已經(jīng)找不回了」的原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/hairetz/java/article/details/4535920

  • 互斥鎖加條件變量使用過程
    1. 臨界區(qū)加鎖
    2. 掛起等待,等待時釋放鎖
    3. 被喚醒,加鎖,進(jìn)入臨界區(qū)
    4. 退出臨界區(qū)時減鎖。
      掛起前后鎖的變化:掛起等待前加鎖,掛起等待時解鎖,被喚醒時解鎖
  • 通知等待該條件變量的線程(v操作)
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);//廣播通知

示例代碼
用條件變量和互斥鎖解決生產(chǎn)者消費(fèi)者問題

#include<iostream>
#include<unistd.h>
#include<stdlib.h>
#include<pthread.h>

using namespace std;

static int count = 0;
// 對于這些pthread中的type要么使用init初始化, 要么使用系統(tǒng)宏初始化
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond_not_full = PTHREAD_COND_INITIALIZER;
static pthread_cond_t cond_not_empty = PTHREAD_COND_INITIALIZER;

void *produce(void *arg) {
    while(1) {
        pthread_mutex_lock(&mutex);

        /*
        這里寫上while是沒有錯的, 這是因?yàn)楫?dāng)其他的線程調(diào)用signal的時候, 可能會有多個線程同時被喚醒, 但是由于只有一個線程才可以拿到鎖, 其他的還是需要繼續(xù)wait, 所以為了避免“驚群效應(yīng)”, 同時也為了維護(hù)同一個時刻只有一個線程可以進(jìn)入臨界區(qū), 這里使用while
        */
        while(count >= 10) {
            pthread_cond_wait(&cond_not_full, &mutex);
        }

        ++count;
        cout << "produce: count = " << count << endl;
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond_not_empty);
    }

    return NULL;
}

void *consume(void *arg) {
    while(1) {
        pthread_mutex_lock(&mutex);

        while(count <= 0) {
            pthread_cond_wait(&cond_not_empty, &mutex);
        }

        --count;
        cout << "consume: count = " << count << endl;
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond_not_full);
    }

    return NULL;
}

int main() {
    pthread_t ptid, ctid;
    pthread_create(&ptid, NULL, produce, NULL);
    pthread_create(&ctid, NULL, consume, NULL);
    pthread_join(ptid, NULL);
    pthread_join(ctid, NULL);
    return 0;
}

信號量

注意此地方是posix信號量,用于線程同步。(SYSTEM V信號量用于進(jìn)程同步)

sem_init(&m_sem, 0, num);
sem_destroy(&m_sem);
sem_wait(&m_sem);//得到資源信號量減一或一直等待
sem_post(&m_sem);//信號量加1

讀寫鎖

pthread_rwlock_init(&rwlock, NULL);
pthread_rwlock_destroy(&rwlock);
pthread_rwlock_rdlock(&rwlock);
pthread_rwlock_wrlock(&rwlock);
pthread_rwlock_unlock(&rwlock);

自旋鎖

(忙等而不是阻塞等,用戶層不常用)

pthread_spin_init(&m_spin, NULL);
pthread_spin_destroy(&m_spin);
pthread_spin_lock(&m_spin);
pthread_spin_unlock(&m_spin);
pthread_spin_trylock(&m_spin);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容