pthread和semaphore的簡單應用以及四個典型的多線程問題


pthread和semaphore的簡單應用以及四個典型的多線程問題

pthread常用函數簡單介紹

創建線程

int  pthread_create(pthread_t  *  thread,

pthread_attr_t * attr,

void * (*start_routine)(void *),

void * arg)

thread是一個pthread_t類型的指針,可以簡單理解為線程ID

attr表示該線程的屬性,具體沒有看,下面的程序中都設置成了NULL,表示默認屬性。

start_routine是線程函數體的函數指針

arg是線程函數的參數

線程函數的類型是 void *fun(void *)也就是可以帶一個指針參數,也可以返回一個指針。

父線程回收子線程資源

當子線程運行結束后,還有以下資源要回收。

int pthread_join(pthread_t th, void **thread_return)

th使pthread_t類型的變量,可以理解為線程ID

thread_return 是子線程函數的返回值。

 

初始化一個互斥鎖

int pthread_mutex_init(pthread_mutex_t *mutex,

const pthread_mutex_attr_t *mutexattr);

mutex表示待初始化的互斥鎖,mutexattr表示互斥鎖的屬性,沒仔細研究,下面的程序中都是使用的NULL。表示默認屬性

互斥鎖枷鎖和解鎖

int pthread_mutex_lock(pthread_mutex *mutex);

int pthread_mutex_unlock(pthread_mutex *mutex);

銷毀互斥鎖

int pthread_mutex_destroy(pthread_mutex *mutex);

 

semaphore常用函數介紹

初始化信號量

int sem_init (sem_t *sem , int pshared, unsigned int value);

sem表示待初始化的信號量

pshared表示共享屬性,Linux中貌似只能設置為0

value表示信號量的初始值

申請資源

int sem_wait(sem_t *sem);

釋放資源

int sem_post(sem_t *sem);

銷毀信號量

int sem_destroy(sem_t *sem);

 

一個常見的面試題

編寫一個程序,開啟3個線程,線程1輸出A,線程2輸出B,線程3輸出C,要求輸出結果必須按ABC的順序顯示;如:ABCABC….

 

典型的線程同步的問題:

線程1進行后線程2才能進行,然后才是線程3,線程3執行后線程1有開始執行。

也就是:

 

可以看到形成了一個環形,也就可能會因為出現環路等待而形成死鎖,解決的辦法就是,指定一個進程先執行,而且題目中讓我們依次輸出ABC,所以我們指定線程1先運行。

代碼如下:

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

static sem_t A_B;
static sem_t B_C;
static sem_t C_A;


void *printA(void *arg)
{
    int i = 0;
    for(i = 1;i < 11;i++)
    {
        sem_wait(&C_A);
        printf("第%02d次:A",i);
        sem_post(&A_B);

    }
    return NULL;
}
void  *printB(void *arg)
{
    int i = 0;
    for(i = 1;i < 11;i++)
    {
        sem_wait(&A_B);
        printf("B");
        sem_post(&B_C);

    }
    return NULL;
}
void *printC(void *arg)
{
    int i = 0;
    for(i = 1;i < 11;i++)
    {
        sem_wait(&B_C);
        printf("C\n");
        sem_post(&C_A);

    }
    return NULL;
}
int main()
{
    pthread_t thread_A;
    pthread_t thread_B;
    pthread_t thread_C;
    sem_init(&A_B,0,0);
    sem_init(&B_C,0,0);
    sem_init(&C_A,0,1);
    pthread_create(&thread_A,NULL,printA,NULL);
    pthread_create(&thread_B,NULL,printB,NULL);
    pthread_create(&thread_C,NULL,printC,NULL);
    pthread_join(thread_A,NULL);
    pthread_join(thread_B,NULL);
    pthread_join(thread_C,NULL);
    sem_destroy(&A_B);
    sem_destroy(&B_C);
    sem_destroy(&C_A);
    printf("\n");
    
    return 0;

}

 

 

生產者消費者問題

生產者消費者之間存在的互斥和同步關系分析

首先,同一時間,只允許一個生產者對當前該生產的位置進行訪問,所以生產者與生產者之間是互斥關系。

再者,同一時間,只允許一個消費者對當前該消費的位置進行消費,所以消費者與消費者之間也是互斥關系

最后,同一個位置要先生產再消費,所以生產者和消費者之間是同步關系。

我在具體是現實,用了一個指針in表示下一個待生產的位置,由於生產者和消費者線程都需要訪問這個指針in,所以in是一個臨界區,生產者和消費者要互斥地訪問,為了變成簡便我直接也把生產者和消費者看成是互斥關系,但是這也導致臨界區的粒度變大。

代碼如下:

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define NUM 10
#define P_NUM 5
#define C_NUM 10

int buffer[10];
int *in;

pthread_mutex_t mutex;//buffer臨界區

sem_t p_sem;//消費者信號量
sem_t c_sem;//生產者信號量




void *producer(void *arg)
{
    while(1)
    {
        sem_wait(&p_sem);

        pthread_mutex_lock(&mutex);
        printf("生產者生產了第%02d個位置的商品\n",*in);
        in++;
        pthread_mutex_unlock(&mutex);

        sem_post(&c_sem);
        sleep(2);
    }
    return NULL;
}
void *consumer(void *arg)
{
    while(1)
    {
        sem_wait(&c_sem);

        pthread_mutex_lock(&mutex);
        in--;
        printf("消費者消耗了第%02d個位置的商品\n",*in);
        pthread_mutex_unlock(&mutex);

        sem_post(&p_sem);
        sleep(2);
    }
    return NULL;
}

int main()
{
    int i = 1;
    for(i = 0;i < NUM;i++)
    {
        buffer[i] = i + 1;
    }

    in = buffer;

    //初始化互斥體
    pthread_mutex_init(&mutex,NULL);
    
    //初始化信號量
    sem_init(&p_sem,0,NUM);
    sem_init(&c_sem,0,0);

    pthread_t ppt[P_NUM];

    //創建生產者線程
    for(i = 0;i < P_NUM;i++)
    {
        pthread_create(&ppt[i],NULL,producer,NULL);
    }
    //創建消費者線程
    pthread_t cpt[C_NUM];
    for(i = 0;i < C_NUM;i++)
    {
        pthread_create(&cpt[i],NULL,consumer,NULL);
    }
    
    //回收資源
    for(i = 0;i < P_NUM;i++)
    {
        pthread_join(ppt[i],NULL);
    }
    for(i = 0;i < C_NUM;i++)
    {
        pthread_join(cpt[i],NULL);
    }
    pthread_mutex_destroy(&mutex);
    sem_destroy(&p_sem);
    sem_destroy(&c_sem);

    return 0;
}

 

讀者寫者問題

讀者寫者也是一個非常著名的同步問題。讀者寫者問題描述非常簡單,有一個寫者很多讀者,多個讀者可以同時讀文件,但寫者在寫文件時不允許有讀者在讀文件,同樣有讀者在讀文件時寫者也不去能寫文件。

 

 

讀者和寫者之間存在互斥關系

寫者和寫者之間存在互斥關系

讀者和讀者之間沒有互斥關系,是共享關系。

 

這個問題的解決分為讀者優先和寫者優先:

 

讀者優先是這樣的:一旦有讀者成功訪問,那么寫者將被阻塞,允許后續讀者,直到沒有讀者后,寫者才被允許訪問。

寫者優先是這樣的:一旦后寫者申請訪問,那么將阻止后續讀者繼續訪問,等當前讀者讀完后,寫者開始寫。

 

讀者優先代碼如下:

/**
 * 讀者寫者問題
 * 讀者優先
 * 青兒哥哥
 * 博客園
 * 2017-09-27
 * */

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

#define R_NUM 10 //讀者個數
#define W_NUM 3 //寫者個數

pthread_t rpt[R_NUM];//讀者線程的id
pthread_t wpt[W_NUM];//寫者線程的id

int readercnt = 0;


int buffer = 100;

pthread_mutex_t buffer_mutex;//緩沖區的臨界區

pthread_mutex_t readercnt_mutex;//讀者數量的臨界區




void write()
{
    int rd = rand()%100;
    buffer = rd;
    printf("寫者寫:%d\n",buffer);
}
void read()
{
    printf("讀者讀:%d\n",buffer);
}

void *reader(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&readercnt_mutex);
        readercnt++;
        if(readercnt == 1)
        {
            pthread_mutex_lock(&buffer_mutex);
        }
        pthread_mutex_unlock(&readercnt_mutex);

        read();

        pthread_mutex_lock(&readercnt_mutex);
        readercnt--;
        if(readercnt == 0)
        {
            pthread_mutex_unlock(&buffer_mutex);
        }
        pthread_mutex_unlock(&readercnt_mutex);
        sleep(1);

    }
    return NULL;
    
}
void *writer(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&buffer_mutex);
        write();
        pthread_mutex_unlock(&buffer_mutex);
        sleep(1);
    }
    return NULL;
}

int main()
{
    //初始化互斥體
    pthread_mutex_init(&buffer_mutex,NULL);
    pthread_mutex_init(&readercnt_mutex,NULL);

    int i = 0;
    for(i = 0;i < R_NUM;i++)
    {
        pthread_create(&rpt[i],NULL,reader,NULL);
    }
    for(i = 0;i < W_NUM;i++)
    {
        pthread_create(&wpt[i],NULL,writer,NULL);
    }

    for(i = 0;i < R_NUM;i++)
    {
        pthread_join(rpt[i],NULL);
    }
    for(i = 0;i < W_NUM;i++)
    {
        pthread_join(wpt[i],NULL);
    }

    pthread_mutex_destroy(&buffer_mutex);
    pthread_mutex_destroy(&readercnt_mutex);
    
    
    return 0;
}

 

寫者優先代碼如下:

/**
 * 讀者寫者問題
 * 寫者優先
 * 青兒哥哥
 * 博客園
 * 2017-09-27
 * */

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

#define R_NUM 10 //讀者個數
#define W_NUM 3 //寫者個數

pthread_t rpt[R_NUM];//讀者線程的id
pthread_t wpt[W_NUM];//寫者線程的id

int readercnt = 0;
int writercnt = 0;


int buffer = 100;

pthread_mutex_t buffer_mutex;//緩沖區的臨界區

pthread_mutex_t readercnt_mutex;//讀者數量的臨界區
pthread_mutex_t writercnt_mutex;//寫者數量的臨界區

sem_t reader_sem;//讀者的信號量





void write()
{
    int rd = rand()%100;
    buffer = rd;
    printf("寫者寫:%d\n",buffer);
}
void read()
{
    printf("讀者讀:%d\n",buffer);
}

void *reader(void *arg)
{
    while(1)
    {
        sem_wait(&reader_sem);
        sem_post(&reader_sem);
        pthread_mutex_lock(&readercnt_mutex);
        readercnt++;
        if(readercnt == 1)
        {
            pthread_mutex_lock(&buffer_mutex);
        }
        pthread_mutex_unlock(&readercnt_mutex);

        read();

        pthread_mutex_lock(&readercnt_mutex);
        readercnt--;
        if(readercnt == 0)
        {
            pthread_mutex_unlock(&buffer_mutex);
        }
        pthread_mutex_unlock(&readercnt_mutex);
        sleep(1);

    }
    return NULL;
    
}
void *writer(void *arg)
{
    while(1)
    {
        
        pthread_mutex_lock(&writercnt_mutex);
        writercnt++;
        if(writercnt == 1)
        {
            sem_wait(&reader_sem);
        }
        pthread_mutex_unlock(&writercnt_mutex);
        pthread_mutex_lock(&buffer_mutex);
        write();
        pthread_mutex_unlock(&buffer_mutex);
        pthread_mutex_lock(&writercnt_mutex);
        writercnt--;
        if(writercnt == 0)
        {
            sem_post(&reader_sem);
        }
        pthread_mutex_unlock(&writercnt_mutex);

        sleep(1);
    }
    return NULL;
}

int main()
{
    //初始化互斥體
    pthread_mutex_init(&buffer_mutex,NULL);
    pthread_mutex_init(&readercnt_mutex,NULL);
    pthread_mutex_init(&writercnt_mutex,NULL);

    sem_init(&reader_sem,0,1);

    int i = 0;
    for(i = 0;i < R_NUM;i++)
    {
        pthread_create(&rpt[i],NULL,reader,NULL);
    }
    for(i = 0;i < W_NUM;i++)
    {
        pthread_create(&wpt[i],NULL,writer,NULL);
    }

    for(i = 0;i < R_NUM;i++)
    {
        pthread_join(rpt[i],NULL);
    }
    pthread_mutex_destroy(&buffer_mutex);
    pthread_mutex_destroy(&readercnt_mutex);
    pthread_mutex_destroy(&writercnt_mutex);
    sem_destroy(&reader_sem);
    
    
    return 0;

}

 

 

如果你覺得對你有用,請贊一個吧~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM