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; }
如果你覺得對你有用,請贊一個吧~~
