1. 程式人生 > >Linux--執行緒安全-(下)

Linux--執行緒安全-(下)

文章目錄

條件變數

在這裡插入圖片描述
需要一個條件:表示臨界區有沒有資源
為什麼條件變數要和互斥鎖搭配使用?
因為等待需要被喚醒,而被喚醒的前提條件就是條件已經滿足,並且這個條件本身就是一個臨界資源,因此改變條件的操作需要被保護。

條件變數的初始化及銷燬:

int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init
(pthread_cond_t * cond,const pthread_condattr_t * attr); pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

沒有資源就去阻塞等待:

int pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex);
//解鎖+等待,當被喚醒時候,它自動獲得鎖

還有限時等待的函式:

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec *abstime);

pthread_cond_wait函式會對互斥鎖做判斷,如果呼叫執行緒加鎖,就解鎖,然後陷入等待,整個過程是原子操作,防止消費者先拿到鎖,發現條件變數不滿足,無法消費,它陷入阻塞等待,這時候生產者得不到鎖。

喚醒在條件變數上的執行緒:

int pthread_cond_broadcast(pthread_cond_t *cond);//廣播喚醒
int pthread_cond_signal(pthread_cond_t *cond);//喚醒第一個等待的條件變數的執行緒

條件變數程式碼演示:

#include <unistd.h>
#include <stdlib.h> #include <stdio.h> #include <pthread.h> #include <errno.h> pthread_cond_t cond; pthread_mutex_t mutex; int basket = 0;//公共條件,需要互斥鎖來保證原子性 void* saler(void*arg) { while(1){ pthread_mutex_lock(mutex); if(basket == 0){ printf("I sell a good\n"); basket = 1; pthread_cond_signal(&cond); pthread_mutex_unlock(mutex); } } return NULL; } void* customer(void*arg) { while(1){ pthread_mutex_lock(mutex); if(basket == 0){ //初始狀態等待,睡眠 //pthread_cond_wait函式會對互斥鎖做判斷,如果呼叫執行緒加鎖,就解鎖,然後陷入等待,整個過程是原子操作 pthread_cond_wait(&cond,&mutex); } printf("I bought a gift for my girl friend!!\n"); basket = 0; pthread_mutex_unlock(mutex); } return NULL; } int main() { pthread_t t1,t2; int ret; pthread_cond_init(&cond,NULL);//條件變數初始化 pthread_mutex_init(&mutex,NULL); ret = pthread_create(&t1,NULL,saler,NULL); if(ret != 0){ perror("pthread_create error"); exit(-1); } ret = pthread_create(&t1,NULL,customer,NULL); if(ret != 0){ perror("pthread_create error"); exit(-1); } pthread_join(t1,NULL); pthread_join(t2,NULL); pthread_cond_destroy(&cond); pthread_mutex_destroy(&mutex); return 0; }

Posix訊號量

POSIX訊號量和SystemV訊號量作用相同,都是用於同步操作,達到無衝突的訪問共享資源的目的。 但POSIX可以用於執行緒間同步。systemV標準posix實現程序間通訊
即可以實現同步也可以實現互斥,既可以用於程序間同步與互斥,也可以用於執行緒間的同步與互斥。

訊號量是什麼(具有一個等待佇列的計數器)
posix執行緒同步實現
消費者:沒有資源則等待
生產者:生產出來則通知佇列中的等待者
1、訊號量的初始化

int sem_init(sem_t *sem, int pshared, unsigned int value);
  • If pshared has the value 0, then the semaphore is shared between the threads of a process
    If pshared is nonzero, then the semaphore is shared between processes
    sem:訊號量變數名
    value:訊號量初始計數
    成功:0 失敗:-1

2、訊號量的操作(等待/通知)

等待:對於消費者,沒有資源則等待。等待訊號量,會將訊號量的值減1。

int sem_wait(sem_t *sem);//阻塞等待
int sem_trywait(sem_t *sem);//沒有資源,報錯返回
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);//限時等待,超時報錯返回

釋出訊號量:生產者生產出資源,通知消費者。釋出訊號量,表示資源使⽤完畢,可以歸還資源了。將訊號量值加1。

int sem_post(sem_t *sem);

3、訊號量的釋放

int sem_destroy(sem_t *sem);
Link with -pthread.
/*訊號量實現執行緒同步與互斥
 * 同步:
 *      1、訊號量的初始化
 *      2、訊號量的操作(等待/通知)
 *      3、訊號量的釋放
 */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>

sem_t sem;//定義訊號量

void*thr_producer(void*arg)
{
    while(1){
        sleep(1);
        printf("I make a hot beef noodles!!\n");
        sem_post(&sem);
    }
    return NULL;
}

void*thr_customer(void*arg)
{
    while(1){
        sem_wait(&sem);
        printf("It is declicious!!\n");
    }
    return NULL;

}

int main()
{
    pthread_t t1,t2;
    int ret;

    sem_init(&sem,0,0);//訊號量初始化
    ret = pthread_create(&t1,NULL,thr_producer,NULL);
    if(ret != 0 ){
        perror("pthread_create error");
        exit(-1);
    }

    ret = pthread_create(&t2,NULL,thr_customer,NULL);
    if(ret != 0 ){
        perror("pthread_create error");
        exit(-1);
    }

    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    sem_destroy(&sem);
    
    return 0;
}

posix執行緒互斥實現

訊號量的操作(等待+通知)
sem_wait()訊號量減1
sem_post()釋出訊號量,表示資源使⽤完畢,可以歸還資源了。將訊號量值加1。

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>

sem_t sem;//定義訊號量

int ticket = 100;//黃牛搶票,總票數100

void* buy_ticket(void*arg)
{
    int id = (int)arg;
    while(1){
        sem_wait(&sem);
        if(ticket > 0){
            usleep(1000);
            ticket--;
            printf("%d Buy a ticket,the ticket has %d\n",id,ticket);
        }
        sem_post(&sem);
    }
    return NULL;
}
int main()
{
    pthread_t tid[4];
    int ret;
    sem_init(&sem,0,1);//訊號量初始化

    int i = 0;
    for(i=0;i<4;i++){
        ret = pthread_create(&tid[i],NULL,buy_ticket,(void*)i);
        if(ret != 0 ){
            perror("pthread_create error");
            exit(-1);
        }
    }
    pthread_join(tid[0],NULL);
    pthread_join(tid[1],NULL);
    pthread_join(tid[2],NULL);
    pthread_join(tid[3],NULL);
    sem_destroy(&sem);

    return 0;
}

訊號量與條件變數在保證同步時候的區別:訊號量是修改自己內部的資源計數,這個內部的資源計數就是條件,而條件變數修改的是外部的條件,需要我們使用者來修改

STL自身不能保證原子性
訊號量的操作是一個原子操作。