生活随笔
收集整理的這篇文章主要介紹了
linux多线程编程——同步与互斥
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、 為什么要用多線程技術?
1、避免阻塞,大家知道,單個進程只有一個主線程,當主線程阻塞的時候,整個進程也就阻塞了,無法再去做其它的一些功能了。
2、避免CPU空轉,應用程序經常會涉及到RPC,數據庫訪問,磁盤IO等操作,這些操作的速度比CPU慢很多,而在等待這些響應時,CPU卻不能去處理新的請求,導致這種單線程的應用程序性能很差。
3、提升效率,一個進程要獨立擁有4GB的虛擬地址空間,而多個線程可以共享同一地址空間,線程的切換比進程的切換要快得多。
二、? 如何使用多線程技術進行編程?
下面給出個多線程程序,一個最簡單的模擬售票系統,代碼如下:
[cpp]?view plaincopy
#include?<stdio.h>?? #include?<pthread.h>?? ?? void?*ticketsell1(void?*);?? void?*ticketsell2(void?*);?? int?tickets?=?20;?? ?? int?main()?? {?? ????pthread_t?id1,id2;?? ????int?error;?? ?? ????error?=?pthread_create(&id1,?NULL,?ticketsell1,?NULL);?? ????if(error?!=?0)?? ????{?? ????????printf("pthread?is?not?created!\n");?? ????????return?-1;?? ????}?? ?? ????error?=?pthread_create(&id2,?NULL,?ticketsell2,?NULL);?? ????if(error?!=?0)?? ????{?? ????????printf("pthread?is?not?created!\n");?? ????????return?-1;?? ????}?? ?? ????pthread_join(id1,NULL);?? ????pthread_join(id2,NULL);?? ?????? ????return?0;?? }?? ?? void?*ticketsell1(void?*arg)?? {?? ????while(1)?? ????{?? ????????if(tickets?>?0)?? ????????{?? ?? ????????????printf("ticketse1?sells?ticket:%d\n",tickets--);?? ????????}?? ????????else?? ????????{?? ????????????break;?? ????????}?? ????}?? ????return?(void?*)0;?? }?? ?? void?*ticketsell2(void?*arg)?? {?? ????while(1)?? ????{?? ????????if(tickets?>?0)?? ????????{?? ?? ????????????printf("ticketse2?sells?ticket:%d\n",tickets--);?? ????????}?? ????????else?? ????????{?? ????????????break;?? ????????}?? ????}?? ?? ????return?(void?*)0;?? }??
執行結果如下:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/mthread$?./mthread1??? ticketse2?sells?ticket:20?? ticketse2?sells?ticket:19?? ticketse2?sells?ticket:18?? ticketse2?sells?ticket:17?? ticketse2?sells?ticket:16?? ticketse2?sells?ticket:15?? ticketse2?sells?ticket:14?? ticketse2?sells?ticket:13?? ticketse2?sells?ticket:12?? ticketse2?sells?ticket:11?? ticketse2?sells?ticket:10?? ticketse2?sells?ticket:9?? ticketse2?sells?ticket:8?? ticketse2?sells?ticket:7?? ticketse2?sells?ticket:6?? ticketse2?sells?ticket:4?? ticketse2?sells?ticket:3?? ticketse2?sells?ticket:2?? ticketse2?sells?ticket:1?? ticketse1?sells?ticket:5??
看到結果,我們發現時能正常賣票的,一部分連續是sel2,另一部分是ticketsel1;
此時,其實存在一個隱含的問題,就是線程間的切換,在單CPU系統中,CPU是有時間片時間,時間片到了,就要執行其它的線程,假設thread1執行到if里面,但在printf執行前發生了線程切換,那么會發生什么呢?我們在這里用usleep函數(放開程序中的usleep注釋行)進行強制模擬切換;
我們看看結果:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/mthread$?gcc?-o?mthread1?mthread1.c?-lpthread?? fs@ubuntu:~/qiang/mthread$?./mthread1??? ticketse2?sells?ticket:20?? ticketse1?sells?ticket:19?? ticketse2?sells?ticket:18?? ticketse1?sells?ticket:17?? ticketse2?sells?ticket:16?? ticketse1?sells?ticket:15?? ticketse2?sells?ticket:14?? ticketse1?sells?ticket:13?? ticketse2?sells?ticket:12?? ticketse1?sells?ticket:11?? ticketse2?sells?ticket:10?? ticketse1?sells?ticket:9?? ticketse2?sells?ticket:8?? ticketse1?sells?ticket:7?? ticketse2?sells?ticket:6?? ticketse1?sells?ticket:5?? ticketse2?sells?ticket:4?? ticketse1?sells?ticket:3?? ticketse1?sells?ticket:2?? ticketse2?sells?ticket:1?? ticketse1?sells?ticket:0?? fs@ubuntu:~/qiang/mthread$???
運行程序發現竟然有0號票被賣出了,這顯然是錯誤的!當thread1的if里面發生線程切換時,thread2得到運行,把最后一張票賣了,此時thread1恢復運行,結果賣出了0號票,這里我們需要的是火車票的票數數據對于所有線程而言是同步的,所以就要用到線程同步技術了。
?
三、? 使用多線程的同步與互斥
1、多線程的同步方式有很多種,例如互斥鎖,條件變量,信號量,讀寫鎖。先看看互斥鎖如何解決多線程之間的同步問題。程序用互斥鎖后如下:
[cpp]?view plaincopy
#include?<stdio.h>?? #include?<pthread.h>?? ?? void?*ticketsell1(void?*);?? void?*ticketsell2(void?*);?? int?tickets?=?20;?? pthread_mutex_t?mutex;?? ?? int?main()?? {?? ????pthread_t?id1,id2;?? ????pthread_mutex_init(&mutex,?NULL);?? ????int?error;?? ?? ????error?=?pthread_create(&id1,?NULL,?ticketsell1,?NULL);?? ????if(error?!=?0)?? ????{?? ????????printf("pthread?is?not?created!\n");?? ????????return?-1;?? ????}?? ?? ????error?=?pthread_create(&id2,?NULL,?ticketsell2,?NULL);?? ????if(error?!=?0)?? ????{?? ????????printf("pthread?is?not?created!\n");?? ????????return?-1;?? ????}?? ?? ????pthread_join(id1,NULL);?? ????pthread_join(id2,NULL);?? ?????? ????return?0;?? }?? ?? void?*ticketsell1(void?*arg)?? {?? ????while(1)?? ????{?? ????????pthread_mutex_lock(&mutex);?? ????????if(tickets?>?0)?? ????????{?? ????????????usleep(1000);?? ????????????printf("ticketse1?sells?ticket:%d\n",tickets--);?? ????????????pthread_mutex_unlock(&mutex);?? ?????????????? ????????}?? ????????else?? ????????{?? ????????????pthread_mutex_unlock(&mutex);?? ????????????break;?? ????????}?? ????????pthread_yield();?? ????}?? ????return?(void?*)0;?? }?? ?? void?*ticketsell2(void?*arg)?? {?? ????while(1)?? ????{?? ????????pthread_mutex_lock(&mutex);?? ????????if(tickets?>?0)?? ????????{?? ????????????usleep(1000);?? ????????????printf("ticketse2?sells?ticket:%d\n",tickets--);?? ????????????pthread_mutex_unlock(&mutex);?? ????????}?? ????????else?? ????????{?? ????????????pthread_mutex_unlock(&mutex);?? ????????????break;?? ????????}?? ????????pthread_yield();?? ????}?? ?? ????return?(void?*)0;?? }??
執行結果如下:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/mthread$?vi?mthread1.c?? fs@ubuntu:~/qiang/mthread$?gcc?-o?mthread1?mthread1.c?-lpthread?? fs@ubuntu:~/qiang/mthread$?./mthread1??? ticketse2?sells?ticket:20?? ticketse1?sells?ticket:19?? ticketse2?sells?ticket:18?? ticketse1?sells?ticket:17?? ticketse2?sells?ticket:16?? ticketse1?sells?ticket:15?? ticketse2?sells?ticket:14?? ticketse1?sells?ticket:13?? ticketse2?sells?ticket:12?? ticketse1?sells?ticket:11?? ticketse2?sells?ticket:10?? ticketse1?sells?ticket:9?? ticketse2?sells?ticket:8?? ticketse1?sells?ticket:7?? ticketse2?sells?ticket:6?? ticketse1?sells?ticket:5?? ticketse2?sells?ticket:4?? ticketse1?sells?ticket:3?? ticketse2?sells?ticket:2?? ticketse1?sells?ticket:1??
2、再看看用信號量來解決多線程的同步問題,程序代碼如下:
[cpp]?view plaincopy
#include?<stdio.h>?? #include?<pthread.h>?? #include?<semaphore.h>?? ?? void?*ticketsell1(void?*);?? void?*ticketsell2(void?*);?? int?tickets?=?20;?? sem_t?mutex,full;?? ?? int?main()?? {?? ????pthread_t?id1,id2;?? ????int?error;?? ????int?ret;?? ?? ????ret?=?sem_init(&mutex,?0?,1);?? ????ret?+=?sem_init(&full,?0?,0);?? ?? ????if(ret?!=?0)?? ????{?? ????????printf("sem_init?fails!\n");?? ????}?? ?? ????error?=?pthread_create(&id1,?NULL,?ticketsell1,?NULL);?? ????if(error?!=?0)?? ????{?? ????????printf("pthread?is?not?created!\n");?? ????????return?-1;?? ????}?? ?? ????error?=?pthread_create(&id2,?NULL,?ticketsell2,?NULL);?? ????if(error?!=?0)?? ????{?? ????????printf("pthread?is?not?created!\n");?? ????????return?-1;?? ????}?? ?? ????pthread_join(id1,NULL);?? ????pthread_join(id2,NULL);?? ?????? ????return?0;?? }?? ?? void?*ticketsell1(void?*arg)?? {?? ????while(1)?? ????{?? ????????sem_wait(&mutex);?? ????????if(tickets?>?0)?? ????????{?? ????????????usleep(1000);?? ????????????printf("ticketse1?sells?ticket:%d\n",tickets--);?? ????????????sem_post(&full);?? ????????}?? ????????else?? ????????{?? ????????????sem_post(&full);?? ????????????break;?? ????????}?? ????}?? ????return?(void?*)0;?? }?? ?? void?*ticketsell2(void?*arg)?? {?? ????while(1)?? ????{?? ????????sem_wait(&full);?? ????????if(tickets?>?0)?? ????????{?? ????????????usleep(1000);?? ????????????printf("ticketse2?sells?ticket:%d\n",tickets--);?? ????????????sem_post(&mutex);?? ????????}?? ????????else?? ????????{?? ????????????sem_post(&mutex);?? ????????????break;?? ????????}?? ????}?? ?? ????return?(void?*)0;?? }??
執行結果:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/mthread$?vi?mthread1.c?? fs@ubuntu:~/qiang/mthread$?gcc?-o?mthread1?mthread1.c?-lpthread?? fs@ubuntu:~/qiang/mthread$?./mthread1??? ticketse1?sells?ticket:20?? ticketse2?sells?ticket:19?? ticketse1?sells?ticket:18?? ticketse2?sells?ticket:17?? ticketse1?sells?ticket:16?? ticketse2?sells?ticket:15?? ticketse1?sells?ticket:14?? ticketse2?sells?ticket:13?? ticketse1?sells?ticket:12?? ticketse2?sells?ticket:11?? ticketse1?sells?ticket:10?? ticketse2?sells?ticket:9?? ticketse1?sells?ticket:8?? ticketse2?sells?ticket:7?? ticketse1?sells?ticket:6?? ticketse2?sells?ticket:5?? ticketse1?sells?ticket:4?? ticketse2?sells?ticket:3?? ticketse1?sells?ticket:2?? ticketse2?sells?ticket:1?? fs@ubuntu:~/qiang/mthread$???
上面的sem_init函數用來初始化兩個信號量的初始化值,這里一個設為1,一個設為0,sem_wait類似于P操作,讓信號量減1,如果小于結果小于0,線程阻塞,否則線程繼續執行,sem_post類似于V操作,提升信號量的值,加1,通過這兩個信號量之間的互相“救對方”,就可以實現這兩個線程的同步執行。
我們編譯運行以上程序,發現兩個售票點交替賣票,兩個純程依次得到機會執行,并且不會有0號票賣出,實現了同步。
?
3、我們再用條件變量來解決同步問題,一般條件變量需要結合互斥量一起使用,代碼如下
[cpp]?view plaincopy
#include?<stdio.h>?? #include?<pthread.h>?? #include?<semaphore.h>?? ?? void?*ticketsell1(void?*);?? void?*ticketsell2(void?*);?? int?tickets?=?20;?? pthread_mutex_t?mutex;?? pthread_cond_t??qready?=?PTHREAD_COND_INITIALIZER;?? ?????????? int?main()?? {?? ????pthread_t?id1,id2;?? ????pthread_mutex_init(&mutex,?NULL);?? ????int?error;?? ?? ????error?=?pthread_create(&id1,?NULL,?ticketsell1,?NULL);?? ????if(error?!=?0)?? ????{?? ????????printf("pthread?is?not?created!\n");?? ????????return?-1;?? ????}?? ?? ????error?=?pthread_create(&id2,?NULL,?ticketsell2,?NULL);?? ????if(error?!=?0)?? ????{?? ????????printf("pthread?is?not?created!\n");?? ????????return?-1;?? ????}?? ?? ????pthread_join(id1,NULL);?? ????pthread_join(id2,NULL);?? ?????? ????return?0;?? }?? ?? void?*ticketsell1(void?*arg)?? {?? ????pthread_mutex_lock(&mutex);?? ????while(tickets?>?0)?? ????{?? ????????if(tickets%2?==?1)?? ????????{?? ????????????usleep(1000);?? ????????????printf("ticketse1?sells?ticket:%d\n",tickets--);?? ????????????pthread_cond_signal(&qready);?? ????????}?? ????????else?? ????????{?? ????????????pthread_cond_wait(&qready,&mutex);?? ????????}?? ????????pthread_mutex_unlock(&mutex);?? ????}?? ????return?(void?*)0;?? }?? ?? void?*ticketsell2(void?*arg)?? {?? ????pthread_mutex_lock(&mutex);?? ????while(tickets?>?0)?? ????{?? ????????if(tickets%2?==?0)?? ????????{?? ????????????usleep(1000);?? ????????????printf("ticketse2?sells?ticket:%d\n",tickets--);?? ????????????pthread_cond_signal(&qready);?? ????????}?? ????????else?? ????????{?? ????????????pthread_cond_wait(&qready,&mutex);?? ????????}?? ????????pthread_mutex_unlock(&mutex);?? ????}?? ?? ????return?(void?*)0;?? }??
執行結果如下:
[cpp]?view plaincopy
fs@ubuntu:~/qiang/mthread$?vi?mthread1.c?? fs@ubuntu:~/qiang/mthread$?gcc?-o?mthread1?mthread1.c?-lpthread?? fs@ubuntu:~/qiang/mthread$?./mthread1??? ticketse2?sells?ticket:20?? ticketse1?sells?ticket:19?? ticketse2?sells?ticket:18?? ticketse1?sells?ticket:17?? ticketse2?sells?ticket:16?? ticketse1?sells?ticket:15?? ticketse2?sells?ticket:14?? ticketse1?sells?ticket:13?? ticketse2?sells?ticket:12?? ticketse1?sells?ticket:11?? ticketse2?sells?ticket:10?? ticketse1?sells?ticket:9?? ticketse2?sells?ticket:8?? ticketse1?sells?ticket:7?? ticketse2?sells?ticket:6?? ticketse1?sells?ticket:5?? ticketse2?sells?ticket:4?? ticketse1?sells?ticket:3?? ticketse2?sells?ticket:2?? ticketse1?sells?ticket:1?? fs@ubuntu:~/qiang/mthread$???
? 條件變量通過允許線程阻塞和等待另一個線程發送信號的方法彌補了互斥鎖的不足,它常和互斥鎖一起使用。使用時,條件變量被用來阻塞一個線程,當條件不滿足時,線程往往解開相應的互斥鎖并等待條件變量發生變化。一旦其它的某個線程改變了條件變量,它將通知相應的條件變量喚醒一個或多個正被此條件變量阻塞的線程。這些線程將重新鎖定互斥鎖并重新測試條件是否滿足。一般說來,條件變量被用來進行線程間的同步.
函數pthread_cond_wait使線程阻塞在一個條件變量上,而函數pthread_cond_signal是用來釋放被阻塞在條件變量上的一個線程。但是要注意的是,條件變量只是起到阻塞和喚醒線程的作用,具體的判斷條件還需用戶給出,我這里給出的是tickets是否是偶數這個條件。
總結
以上是生活随笔為你收集整理的linux多线程编程——同步与互斥的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。