天天看點

線程的同步與互斥

線程同步與互斥:

我們先來一個示例看一下:

#include <stdio.h>  
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>

int i = ;

void* fun(void* arg)
{
    char* id = (char *)arg;
    while()
    {
        if(i > )
        {
            sleep();
            printf("%s say i=%d\n",id,i);
            i--;
        }
        else
        {
            break;
        }
    }
    return NULL;
}

int main()
{
    int i = ;
    pthread_t tid1,tid2,tid3,tid4;
    pthread_create(&tid1,NULL,fun,"1");
    pthread_create(&tid2,NULL,fun,"2");
    pthread_create(&tid3,NULL,fun,"3");
    pthread_create(&tid4,NULL,fun,"4");

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    return ;
}
           

按照我們了解的這個函數應該會輸出:i從10—1的順序。那麼結果呢?

線程的同步與互斥

怎麼會出現呢-1,-2呢?

線程的同步與互斥

這就是這個程式會出錯的表面原因。那根本原因是什麼呢?

這裡就涉及到了原子性的概念,我們來看一下在cpu的角度下對待–操作是什麼樣的?

擴充一個指令,檢視目前程式的彙編代碼:

[test@localhost lianxi]$ objdump -d test > test.objdump
           
400724:   8b 05 22 09 20 00       mov    0x200922(%rip),%eax        # 60104c <i>
  40072a:   83 e8 01                sub    $0x1,%eax
  40072d:   89 05 19 09 20 00       mov    %eax,0x200919(%rip)        # 60104c <i>
           

這是函數執行的彙編代碼中“ - - ”操作。我們可以看到–操作在cpu中執行了3步,第一步将i的值移動到寄存器中,第二步cpu進行-1操作,第三步将操作完成的值寫回到i的位址處。

這個中間的過程中如果進行了線程切換,那将會産生錯誤。

歸根結底還是–操作不是原子性操作。

想解決這個問題,很簡單就是建立一個鎖。每次對一個值進行操作的前提必須擁有鎖。而我們知道線程中是有共享的記憶體的,是以我們就在共享記憶體的地方建立一個鎖,使每個線程都可以看到這把鎖。Linux中稱這把鎖為“互斥量”。

互斥量:

一、互斥鎖
互斥鎖,是一種信号量,常用來防止兩個程序或線程在同一時刻通路相同的共享資源。
需要的頭檔案:pthread.h
互斥鎖辨別符:pthread_mutex_t

()互斥鎖初始化:
函數原型: int pthread_mutex_init (pthread_mutex_t* mutex,const pthread_mutexattr_t* mutexattr);
函數傳入值:  mutex:互斥鎖。
mutexattr:PTHREAD_MUTEX_INITIALIZER 建立快速互斥鎖。
PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP 建立遞歸互斥鎖。
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP  建立檢錯互斥鎖。
函數傳回值:成功:;出錯:-

()互斥操作函數
int pthread_mutex_lock(pthread_mutex_t* mutex); //上鎖
int pthread_mutex_trylock (pthread_mutex_t* mutex); //隻有在互斥被鎖住的情況下才阻塞
int pthread_mutex_unlock (pthread_mutex_t* mutex); //解鎖
int pthread_mutex_destroy (pthread_mutex_t* mutex); //清除互斥鎖
函數傳入值:mutex:互斥鎖。
函數傳回值:成功:;出錯:-
           

我們改進一下之前的代碼:

int i = ;
pthread_mutex_t mutex;

void* fun(void* arg)
{
    char* id = (char *)arg;
    while()
    {
        pthread_mutex_lock(&mutex);
        if(i > )
        {
            printf("%s say i=%d\n",id,i);
            i--;
            pthread_mutex_unlock(&mutex);
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
        sleep();
    }
    return NULL;
}

int main()
{
    int i = ;
    pthread_t tid1,tid2,tid3,tid4;
    pthread_create(&tid1,NULL,fun,"1");
    pthread_create(&tid2,NULL,fun,"2");
    pthread_create(&tid3,NULL,fun,"3");
    pthread_create(&tid4,NULL,fun,"4");

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    pthread_join(tid3,NULL);
    pthread_join(tid4,NULL);
    return ;
}
           

效果如下:

線程的同步與互斥

這樣效果就出來了吧!

線程同步:(條件變量)

  • 當線程通路互斥的某個變量時,它在其他線程沒有釋放“鎖”之前什麼也幹不了。
  • 例如動物園裡飼養員給動物為食,在飼養員正在放食物的時候,動物被關在籠子了,什麼也幹不了。隻有當飼養員把食物放好後,打開籠子,動物才能吃東西。

有關函數:

int pthread_cond_destroy(pthread_cond_t *cond);
//删除條件變量
int pthread_cond_init(pthread_cond_t *restrict cond,
              const pthread_condattr_t *restrict attr);
              //初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
//建立
int pthread_cond_wait(pthread_cond_t *restrict cond,
              pthread_mutex_t *restrict mutex);
//等待條件滿足(動物在籠子等待飼養員喂食物的過程)
int pthread_cond_broadcast(pthread_cond_t *cond);
//喚醒多個線程(飼養員放好了食物,打開了許多動物的籠子)
int pthread_cond_signal(pthread_cond_t *cond);
//喚醒單個線程(飼養員放好了食物,隻打開了一個動物的籠子)
           

示例:

pthread_cond_t cond;
pthread_mutex_t mutex;

void* fun()
{
    while()
    {
        pthread_cond_wait(&cond,&mutex);
        printf("tager\n");
    }
}

void* fun2()
{
    while()
    {
        pthread_cond_signal(&cond);
        sleep();
    }
}

int main()
{
    int i = ;

    pthread_cond_init(&cond,NULL);
    pthread_mutex_init(&mutex,NULL);

    pthread_t tid1,tid2;

    pthread_create(&tid1,NULL,fun,NULL);
    pthread_create(&tid2,NULL,fun2,NULL);

    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);

    pthread_cond_destroy(&cond);
    return ;
}
           

效果執行個體:

線程的同步與互斥

在這個pthread_cond_wait函數中為什麼第二個參數要是一個互斥鎖呢?

條件等待是線程通信間的一種必要的方式,如果一個線程所需要的資源不滿足,他就會一直等待,是以必須要有另一個線程來改變它,這時就是倆個線程共享一塊資源了,是以必須要加上鎖。

繼續閱讀