c++多線程同步

線程的5種狀態:

1.創建:創建線程  pthread_create;

2.就緒:資源已經準備就緒,等待cpu調度;

3.運行:cpu調度

4.阻塞:線程因爲某種原因暫時被掛起,比如得不到自己需要的資源,或者獲得不到互斥鎖

產生死鎖的四大條件缺一不可

1.互斥:任何情況下資源只能分給一個線程,若其他線程需要這塊資源就需要等待,直到這塊資源佔有者被釋放;

2.資源不可剝奪:任何一線程的資源不可被其他線程剝奪;

3.循環等待:線程在需要資源的時候,如果該資源被佔用,該線程就保持循環等待的狀態,直到拿到這個資源;

4.請求和保持:任何一線程,在申請資源的時候如果申請不到也不會釋放自己手裏的資源;

避免死鎖的方法

1.資源提前分配好:提前把線程需要的資源分配好;

2.資源有序分配:將資源進行分類,對資源進行排序,按序分配;

3.分享自己的資源:如果該線程得不到資源就將該線程所佔有的資源釋放;

預防死鎖的方法

銀行家算法:1.比如老馬、小馬、老王來銀行貸款,銀行一共只有100個億,老馬貸款80億,OK我的錢夠貸給他,小馬也要貸80億(不安全),不夠你先等着,老王來貸10個億(安全),Ok夠,貸給老王。

在未加鎖的情況下

#include<iostream>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
using namespace std;

pthread_mutex_t mutex;
int ticket_num = 20;


void *fun1(void *args) {
    
    for (int i = 0; i < 20; ++i) {
        if (ticket_num > 0) {
            
            sleep(1); 
            cout<<"buy the "<<20 - ticket_num + 1<<"  th"<<endl;
            --ticket_num;
        }
    }
}

int main()
{
    pthread_t tids[4];
    pthread_mutex_init(&mutex, NULL);
    int flag;
    
    for (int i = 0; i < 4; ++i) {
        flag = pthread_create(&tids[i], NULL, &fun1, NULL);    

        if(flag) {

             //創建線程失敗

         }

    }
    
    void *ans;
    sleep(5);
    for (int i = 0; i < 4; ++i) {
        int flag = pthread_join(tids[i], &ans);
        if (flag) {
            //等待線程結束出錯
        }
        cout<<"ans = "<<ans<<endl;
    }

    return 0;
}

運行截圖

線程之間運行的很亂,這就是線程未進行同步的原因,好比四個同時在12306搶票,這時餘票都顯示餘票還有一張,四個人同時搶,都搶到了,錢也扣了,你們這時候問題來了,這張票你是給誰呢。所以要進行線程同步

1.互斥鎖:

#include<iostream>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
using namespace std;

pthread_mutex_t mutex;
int ticket_num = 20;


void *fun1(void *args) {
    
    for (int i = 0; i < 20; ++i) {
        pthread_mutex_lock(&mutex);
        if (ticket_num > 0) {
            
            sleep(1); 
            cout<<"buy the "<<20 - ticket_num + 1<<"  th"<<endl;
            --ticket_num;
        }
        pthread_mutex_unlock(&mutex);
    }
}

int main()
{
    pthread_t tids[4];
    pthread_mutex_init(&mutex, NULL);

   // pthread_mutex_t mutex_x=PTHREAD_MUTEX_INITIALIZER;//靜態初始化互斥鎖
    int flag;
    
    for (int i = 0; i < 4; ++i) {
        flag = pthread_create(&tids[i], NULL, &fun1, NULL);

        if(flag) {

             //創建線程失敗

         }
    }
    
    void *ans;
    //sleep(10);
    
    for (int i = 0; i < 4; ++i) {
        int flag = pthread_join(tids[i], &ans);
        if (flag) {
            //等待線程結束出錯
        }
        cout<<"ans = "<<ans<<endl;
    }
    return 0;
}

2.條件變量

互斥量不是萬能的,比如某個線程正在等待共享數據內某個條件出現,可可能需要重複對數據對象加鎖和解鎖(輪詢),但是這樣輪詢非常耗費時間和資源,而且效率非常低,所以互斥鎖不太適合這種情況

我們需要這樣一種方法:當線程在等待滿足某些條件時使線程進入睡眠狀態,一旦條件滿足,就換線因等待滿足特定條件而睡眠的線程

如果我們能夠實現這樣一種方法,程序的效率無疑會大大提高,而這種方法正是條件變量!

#include<iostream>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
using namespace std;

pthread_mutex_t mutex;
pthread_cond_t cond;
int ticket_num = 20;

void *f1 (void* args) {
    cout<<"f1  start"<<endl;
    pthread_mutex_lock(&mutex);
    
    while (ticket_num > 10) {
        pthread_cond_wait(&cond, &mutex);
    }
    pthread_mutex_unlock(&mutex);
    cout<<"f1 end"<<endl;
}

void *f2 (void* args) {
    cout<<"f2  start"<<endl;
    pthread_mutex_lock(&mutex);
    
    ticket_num = 1;
    
    pthread_mutex_unlock(&mutex);
    
    cout<<"f2 end"<<endl;
    if (ticket_num < 10) {
        pthread_cond_signal(&cond);
    }
    

}

int main()
{
    pthread_t tids[2];
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    
    int flag;
    
    for (int i = 0; i < 1; ++i) {
        pthread_create(&tids[0], NULL, &f1, NULL);
        sleep(2);
        pthread_create(&tids[1], NULL, &f2, NULL);
    }
    
    void *ans;
    //sleep(10);
    
    for (int i = 0; i < 2; ++i) {
        int flag = pthread_join(tids[i], &ans);
        if (flag) {
            
        }
        //cout<<"ans = "<<ans<<endl;
    }
    return 0;
}

可以多個線程同時讀,但是不能多個線程同時寫

1.讀寫鎖比互斥鎖更加具有適用性和並行性

2.讀寫鎖最適用於對數據結構的讀操作讀操作次數多餘寫操作次數的場合!

3.鎖處於讀模式時可以線程共享,而鎖處於寫模式時只能獨佔,所以讀寫鎖又叫做共享-獨佔鎖

4.讀寫鎖有兩種策略:強讀同步和強寫同步

在強讀同步中,總是給讀者更高的優先權,只要寫者沒有進行寫操作,讀者就可以獲得訪問權限

在強寫同步中,總是給寫者更高的優先權,讀者只能等到所有正在等待或者執行的寫者完成後才能進行讀

不同的系統採用不同的策略,比如航班訂票系統使用強寫同步,圖書館查閱系統採用強讀同步

根據不同的業務場景,採用不同的策略

1)初始化的銷燬讀寫鎖

靜態初始化:pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER

動態初始化:int pthread_rwlock_init(rwlock,NULL),NULL代表讀寫鎖採用默認屬性

銷燬讀寫鎖:int pthread_rwlock_destory(rwlock)

在釋放某個讀寫鎖的資源之前,需要先通過pthread_rwlock_destory函數對讀寫鎖進行清理。釋放由pthread_rwlock_init函數分配的資源

如果你想要讀寫鎖使用非默認屬性,則attr不能爲NULL,得給attr賦值

int pthread_rwlockattr_init(attr),給attr初始化

int pthread_rwlockattr_destory(attr),銷燬attr

2)以寫的方式獲取鎖,以讀的方式獲取鎖,釋放讀寫鎖

int pthread_rwlock_rdlock(rwlock),以讀的方式獲取鎖

int pthread_rwlock_wrlock(rwlock),以寫的方式獲取鎖

int pthread_rwlock_unlock(rwlock),釋放鎖

上面兩個獲取鎖的方式都是阻塞的函數,也就是說獲取不到鎖的話,調用線程不是立即返回,而是阻塞執行,在需要進行寫操作的時候,這種阻塞式獲取鎖的方式是非常不好的,你想一下,我需要進行寫操作,不但沒有獲取到鎖,我還一直在這裏等待,大大拖累效率

所以我們應該採用非阻塞的方式獲取鎖:

int pthread_rwlock_tryrdlock(rwlock)

int pthread_rwlock_trywrlock(rwlock)

代碼待更

信號量

代碼待更

 

文章部分借鑑於https://www.cnblogs.com/yinbiao/p/11190336.html

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