日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux多线程编程——同步与互斥

發(fā)布時間:2023/12/9 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux多线程编程——同步与互斥 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、 為什么要用多線程技術?

1、避免阻塞,大家知道,單個進程只有一個主線程,當主線程阻塞的時候,整個進程也就阻塞了,無法再去做其它的一些功能了。

2、避免CPU空轉(zhuǎn),應用程序經(jīng)常會涉及到RPC,數(shù)據(jù)庫訪問,磁盤IO等操作,這些操作的速度比CPU慢很多,而在等待這些響應時,CPU卻不能去處理新的請求,導致這種單線程的應用程序性能很差。

3、提升效率,一個進程要獨立擁有4GB的虛擬地址空間,而多個線程可以共享同一地址空間,線程的切換比進程的切換要快得多。

二、? 如何使用多線程技術進行編程?

下面給出個多線程程序,一個最簡單的模擬售票系統(tǒng),代碼如下:

[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)??
  • ????????{??
  • //??????????usleep(1000);??
  • ????????????printf("ticketse1?sells?ticket:%d\n",tickets--);??
  • ????????}??
  • ????????else??
  • ????????{??
  • ????????????break;??
  • ????????}??
  • ????}??
  • ????return?(void?*)0;??
  • }??
  • ??
  • void?*ticketsell2(void?*arg)??
  • {??
  • ????while(1)??
  • ????{??
  • ????????if(tickets?>?0)??
  • ????????{??
  • //??????????usleep(1000);??
  • ????????????printf("ticketse2?sells?ticket:%d\n",tickets--);??
  • ????????}??
  • ????????else??
  • ????????{??
  • ????????????break;??
  • ????????}??
  • ????}??
  • ??
  • ????return?(void?*)0;??
  • }??
  • 執(zhí)行結果如下:

    [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??
  • 看到結果,我們發(fā)現(xiàn)時能正常賣票的,一部分連續(xù)是sel2,另一部分是ticketsel1;

    此時,其實存在一個隱含的問題,就是線程間的切換,在單CPU系統(tǒng)中,CPU是有時間片時間,時間片到了,就要執(zhí)行其它的線程,假設thread1執(zhí)行到if里面,但在printf執(zhí)行前發(fā)生了線程切換,那么會發(fā)生什么呢?我們在這里用usleep函數(shù)(放開程序中的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$???
  • 運行程序發(fā)現(xiàn)竟然有0號票被賣出了,這顯然是錯誤的!當thread1的if里面發(fā)生線程切換時,thread2得到運行,把最后一張票賣了,此時thread1恢復運行,結果賣出了0號票,這里我們需要的是火車票的票數(shù)數(shù)據(jù)對于所有線程而言是同步的,所以就要用到線程同步技術了。

    ?

    三、? 使用多線程的同步與互斥

    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();//線程調(diào)度函數(shù),使每個線程都有執(zhí)行機會??
  • ????}??
  • ????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();//線程調(diào)度函數(shù),是兩個線程都有執(zhí)行機會??
  • ????}??
  • ??
  • ????return?(void?*)0;??
  • }??
  • 執(zhí)行結果如下:

    [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);//初始化mutex信號量為1??
  • ????ret?+=?sem_init(&full,?0?,0);//初始化full信號量為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);//mutex信號量進行P操作??
  • ????????if(tickets?>?0)??
  • ????????{??
  • ????????????usleep(1000);??
  • ????????????printf("ticketse1?sells?ticket:%d\n",tickets--);??
  • ????????????sem_post(&full);//full信號量進行V操作??
  • ????????}??
  • ????????else??
  • ????????{??
  • ????????????sem_post(&full);//full信號量進行V操作??
  • ????????????break;??
  • ????????}??
  • ????}??
  • ????return?(void?*)0;??
  • }??
  • ??
  • void?*ticketsell2(void?*arg)??
  • {??
  • ????while(1)??
  • ????{??
  • ????????sem_wait(&full);//full信號量進行P操作??
  • ????????if(tickets?>?0)??
  • ????????{??
  • ????????????usleep(1000);??
  • ????????????printf("ticketse2?sells?ticket:%d\n",tickets--);??
  • ????????????sem_post(&mutex);//mutex信號量進行V操作??
  • ????????}??
  • ????????else??
  • ????????{??
  • ????????????sem_post(&mutex);//mutex信號量進行V操作??
  • ????????????break;??
  • ????????}??
  • ????}??
  • ??
  • ????return?(void?*)0;??
  • }??
  • 執(zhí)行結果:

    [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函數(shù)用來初始化兩個信號量的初始化值,這里一個設為1,一個設為0,sem_wait類似于P操作,讓信號量減1,如果小于結果小于0,線程阻塞,否則線程繼續(xù)執(zhí)行,sem_post類似于V操作,提升信號量的值,加1,通過這兩個信號量之間的互相“救對方”,就可以實現(xiàn)這兩個線程的同步執(zhí)行。

    我們編譯運行以上程序,發(fā)現(xiàn)兩個售票點交替賣票,兩個純程依次得到機會執(zhí)行,并且不會有0號票賣出,實現(xiàn)了同步。

    ?

    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;//靜態(tài)初始化條件變量;??
  • ??????????
  • 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);//條件改變,發(fā)送信號,通知ticketse2??
  • ????????}??
  • ????????else??
  • ????????{??
  • ????????????pthread_cond_wait(&qready,&mutex);//解開Mutex,并等待qready改變??
  • ????????}??
  • ????????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);//條件改變,發(fā)送信號,通知ticketse1??
  • ????????}??
  • ????????else??
  • ????????{??
  • ????????????pthread_cond_wait(&qready,&mutex);//解開mutex,并等待qready改變??
  • ????????}??
  • ????????pthread_mutex_unlock(&mutex);//給互斥量解鎖??
  • ????}??
  • ??
  • ????return?(void?*)0;??
  • }??
  • 執(zhí)行結果如下:

    [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$???

  • ? 條件變量通過允許線程阻塞和等待另一個線程發(fā)送信號的方法彌補了互斥鎖的不足,它常和互斥鎖一起使用。使用時,條件變量被用來阻塞一個線程,當條件不滿足時,線程往往解開相應的互斥鎖并等待條件變量發(fā)生變化。一旦其它的某個線程改變了條件變量,它將通知相應的條件變量喚醒一個或多個正被此條件變量阻塞的線程。這些線程將重新鎖定互斥鎖并重新測試條件是否滿足。一般說來,條件變量被用來進行線程間的同步.

    函數(shù)pthread_cond_wait使線程阻塞在一個條件變量上,而函數(shù)pthread_cond_signal是用來釋放被阻塞在條件變量上的一個線程。但是要注意的是,條件變量只是起到阻塞和喚醒線程的作用,具體的判斷條件還需用戶給出,我這里給出的是tickets是否是偶數(shù)這個條件。

    總結

    以上是生活随笔為你收集整理的linux多线程编程——同步与互斥的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。