1. 程式人生 > >【ARM&Linux】常用執行緒同步的三種方法

【ARM&Linux】常用執行緒同步的三種方法

【執行緒同步高效率程式設計】

Linux系統中執行緒最大的特點就是共享性,執行緒同步問題較為困難也很重要,最常用的三種是:條件變數、互斥鎖、無名訊號量。(ps: 有名訊號量可用於程序同步,無名訊號量只能用於執行緒同步,是輕量級的。)

(一)、【互斥鎖】:mutex

執行緒互斥量資料型別:pthread_mutex_t

  1. 初始化鎖
    靜態分配: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    動態分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);


    引數一:建立的互斥鎖
    引數二:儲存互斥鎖資訊的結構,一般為NULL

  2. 加鎖:對共享資源的訪問,要對互斥量進行加鎖,如果互斥量已經上了鎖,呼叫執行緒會阻塞,直到互斥量被解鎖。
    int pthread_mutex_lock(pthread_mutex *mutex);
    int pthread_mutex_trylock(pthread_mutex_t *mutex);

    引數:指明互斥鎖

  3. 解鎖:在完成了對共享資源的訪問後,要對互斥量進行解鎖
    int pthread_mutex_unlock(pthread_mutex_t *mutex);
    引數:指明互斥鎖

  4. 銷燬鎖
    int pthread_mutex_destroy(pthread_mutex *mutex);
    引數:指明互斥鎖

(二)、【條件變數】:cond

條件變數用來自動阻塞一個執行緒,直到某特殊情況發生為止。通常條件變數和互斥鎖同時使用。
資料型別:pthread_cond_t

  1. 初始化
    靜態:pthread_cond_t cond = PTHREAD_COND_INITIALIER;
    動態:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);

    引數一:指明條件變數
    引數二:儲存條件變數屬性的結構>
  2. 等待條件成立:釋放鎖,同時等待條件為真才能有停止阻塞。
    int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
    引數一:指明條件變數
    引數二:指明互斥鎖
    int pthread_cond_timewait(pthread_cond_t *cond,pthread_mutex *mutex,const timespec *abstime);
  3. 啟用條件變數:
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond); //解除所有執行緒的阻塞
  4. 登出條件變數
    int pthread_cond_destroy(pthread_cond_t *cond);

(三)、【無名訊號量】:sem,

注意:連結需要加上-pthread選項
例如:gcc -pthread main.c -o main

有名訊號量可用於程序的同步,標頭檔案:#include<sys/sem.h>,;而無名訊號量只能用於執行緒,是輕量級,標頭檔案:#include <semaphore.h>)

  1. 初始化:
    int sem_init (sem_t *sem , int pshared, unsigned int value);
    引數一:指明訊號量
    引數二:共享選項(linux 只支援為0,即表示它是當前程序的區域性訊號量)
    引數三:設定初始值
  2. 等待訊號量:給訊號量減1,然後等待直到訊號量的值大於0。
    int sem_wait(sem_t *sem);
  3. 釋放訊號量:
    int sem_post(sem_t *sem);
  4. 銷燬訊號量:
    int sem_destroy(sem_t *sem);

【DEMO】

現有兩個同學打掃衛生,學生A負責掃地,學生B負責拖地,很明顯要掃完地之後在拖地,由此引入執行緒同步。

1、條件變數和互斥鎖的聯合利用實現
#include <stdio.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

#define DEBUG_INFO(...) printf("Info: "); printf(__VA_ARGS__)

void *student_1();
void *student_2();


int num = 0;    //共享資源
pthread_mutex_t mulock = PTHREAD_MUTEX_INITIALIZER;     //互斥鎖
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;         //條件變數
pthread_t stu_thread[2];        //兩個學生執行緒
int main()
{
    int i;

    // 建立兩個學生執行緒
    pthread_create(&stu_thread[0], NULL, student_1, NULL);
    pthread_create(&stu_thread[1], NULL, student_2, NULL);

    // 等待兩個執行緒結束
    for(i=0; i<2; i++)
    {
        pthread_join(stu_thread[i], NULL);
    }

    // 登出操作
    pthread_mutex_destroy(&mulock);
    pthread_cond_destroy(&cond);
    return 0;
}

void *student_1()
{
    int i;
    DEBUG_INFO("student a start work .\n");

    for(i=0; i<5; i++)
    {
        DEBUG_INFO("i = %d \n", i);
        pthread_mutex_lock(&mulock);    //鎖住
        num++;      //掃一次地
        if(num >= 5)
        {
            DEBUG_INFO("student a finished work .\n");
            pthread_cond_signal(&cond); //通知學生B已經掃完地了,並解鎖
        }

        pthread_mutex_unlock(&mulock);
        sleep(1);
    }

    pthread_exit(NULL);
    return 0;
}
void *student_2()
{
    DEBUG_INFO("in student 2 .. \n");
    while(num < 5)      //不用if四因為需要防止莫名錯誤
    {
        pthread_cond_wait(&cond, &mulock);  //等待學生A掃地結束,等不到會再次一直阻塞
    }

    num = 0;    //拖地
    pthread_mutex_unlock(&mulock);
    DEBUG_INFO("student b finished work .\n");
    pthread_exit(NULL);
    return 0;
}

Makefile
這裡寫圖片描述
執行結果
這裡寫圖片描述
由執行結果可見,學生A先完成工作,學生B在完成工作,所以成功實現執行緒同步。

2、訊號量實現
/****************************************************************************************
* 檔名: demo2.c
* 建立者: 
* 時 間: 
* 聯 系: 
* 簡 介: 
*****************************************************************************************/

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

#define DEBUG_INFO(...) printf("Info: "); printf(__VA_ARGS__)

int num = 0;    //共享資源
sem_t mysem; //用於同步的訊號量
pthread_t stu_thread[2];        //兩個學生執行緒

void *student_1();
void *student_2();


int main()
{
    // 初始化訊號量
    sem_init(&mysem, 0, 0);

    int i;

    // 建立兩個學生執行緒
    pthread_create(&stu_thread[0], NULL, student_1, NULL);
    pthread_create(&stu_thread[1], NULL, student_2, NULL);

    // 等待兩個執行緒結束
    for(i=0; i<2; i++)
    {
        pthread_join(stu_thread[i], NULL);
    }

    // 登出操作
    sem_destroy(&mysem);
    return 0;
}


void *student_1()
{
    int i;
    DEBUG_INFO("student a start work .\n");

    for(i=0; i<5; i++)
    {
        DEBUG_INFO("i = %d \n", i);
        num++;      //掃一次地
        if(num >= 5)
        {
            DEBUG_INFO("student a finished work .\n");
            sem_post(&mysem);   //釋放訊號量
        }

        sleep(1);
    }

    pthread_exit(NULL);
    return 0;
}
void *student_2()
{
    DEBUG_INFO("in student 2 .. \n");
    sem_wait(&mysem);       //等待訊號量 
    num = 0;    //拖地
    DEBUG_INFO("student b finished work .\n");
    pthread_exit(NULL);
    return 0;
}

【執行結果】
這裡寫圖片描述
由執行結果可見,用訊號量的程式執行結果與使用條件變數結果一致,所以實驗成功!

end..