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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 多线程编程笔记

發(fā)布時間:2025/3/20 linux 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 多线程编程笔记 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一,?線程基礎(chǔ)知識

1,線程的概念

線程是進(jìn)程的一個實體,是CPU調(diào)度和分派的基本單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位。線程自己基本上不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行

中必不可少的資源(如程序計數(shù)器,一組寄存器和棧),但是它可與同屬一個進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源。

2,線程的優(yōu)點(diǎn)
(1)?????? 通過為每種事件類型的處理分配單獨(dú)的線程,能夠簡化處理異步時間的代碼。
(2)?????? 多個線程可以自動共享相同的存儲地址空間和文件描述符。
(3)?????? 有些問題可以通過將其分解從而改善整個程序的吞吐量。
(4)?????? 交互的程序可以通過使用多線程實現(xiàn)相應(yīng)時間的改善,多線程可以把程序中處理用戶輸入輸出的部分與其它部分分開。

3,線程的缺點(diǎn)
???? 線程也有不足之處。編寫多線程程序需要更全面更深入的思考。在一個多線程程序里,因時間分配上的細(xì)微偏差或者因共享了不該共享的變量而造成不良

???? 影響的可能性是很大的。調(diào)試一個多線程程序也比調(diào)試一個單線程程序困難得多。

4,線程的結(jié)構(gòu)
???? 線程包含了表示進(jìn)程內(nèi)執(zhí)行環(huán)境必需的信息,其中包括進(jìn)程中標(biāo)識線程的線程ID,一組寄存器值、棧、調(diào)度優(yōu)先級和策略、信號屏蔽子,errno變量以及線

?? 程私有數(shù)據(jù)。進(jìn)程的所有信息對該進(jìn)程的所有線程都是共享的,包括可執(zhí)行的程序文本,程序的全局內(nèi)存和堆內(nèi)存、棧以及文件描述符。

?? 進(jìn)程ID在整個系統(tǒng)中是唯一的,但線程不同,線程ID只在它所屬的進(jìn)程環(huán)境中有效。

5,線程的創(chuàng)建
?使用pthread_create函數(shù)。

#include<pthread.h> int pthread_create (pthread_t *__restrict tidp,//新創(chuàng)建的線程ID__const pthread_attr_t *__restrict __attr,//線程屬性void *(*__start_routine) (void *),//新創(chuàng)建的線程從start_routine開始執(zhí)行void *__restrict __arg)//執(zhí)行函數(shù)的參數(shù)


?當(dāng)pthread_creat成功返回時, tidp指向的內(nèi)存單元被設(shè)置為新創(chuàng)建線程的線程ID。_attr參數(shù)用于定制各種不同的線程屬性。可以把它設(shè)置為NULL,創(chuàng)建默認(rèn)的線程屬性。

新創(chuàng)建的線程從__start_routine函數(shù)的地址開始運(yùn)行,該函數(shù)只有一個無類型指針參數(shù)arg,如果需要向__start_routine函數(shù)傳遞的參數(shù)不止一個,那么需要把這些參數(shù)放到

一個結(jié)構(gòu)中,然后把這個結(jié)構(gòu)的地址作為arg參數(shù)傳入。返回值:成功-0,失敗-返回錯誤編號,可以用strerror(errno)函數(shù)得到錯誤信息.

6,.線程的終止
?????? 線程是依進(jìn)程而存在的,當(dāng)進(jìn)程終止時,線程也就終止了。當(dāng)然也有在不終止整個進(jìn)程的情況下停止它的控制流。

(1)線程只是從啟動例程中返回,返回值是線程的退出碼。

(2)線程可以被同一進(jìn)程中的其他線程取消。

(3)線程調(diào)用pthread_exit.

?7,終止線程, 怎樣正確處理線程終止。


? 調(diào)用 pthread_exit

#include <pthread.h> void pthread_exit(void *rval_ptr);

rval_prt是一個無類型指針,與傳給啟動例程的單個參數(shù)類似。進(jìn)程中的其他線程可以調(diào)用pthread_join函數(shù)訪問到這個指針。

?

? 在 Linux 平臺下,當(dāng)處理線程結(jié)束時需要注意的一個問題就是如何讓一個線程善始善終,讓其所占資源得到正確釋放。在 Linux 平臺默認(rèn)情況下,雖然各個線程之間是

相互獨(dú)立的,一個線程的終止不會去通知或影響其他的線程。但是已經(jīng)終止的線程的資源并不會隨著線程的終止而得到釋放,我們需要調(diào)用 pthread_join() 來獲得另一

個線程的終止?fàn)顟B(tài)并且釋放該線程所占的資源。

pthread_join:獲得進(jìn)程的終止?fàn)顟B(tài)

#include <pthread.h> int pthread_join(pthread_t thread,void **rval_ptr);

返回值:若成功返回0,否則返回錯誤編號。


當(dāng)一個線程通過調(diào)用pthread_exit退出或者簡單地從啟動歷程中返回時,進(jìn)程中的其他線程可以通過調(diào)用pthread_join函數(shù)獲得進(jìn)程的退出狀態(tài)。調(diào)用pthread_join進(jìn)程

將一直阻塞,直到指定的線程調(diào)用pthread_exit,從啟動例程中或者被取消。如果線程只是從它的啟動歷程返回,rval_ptr將包含返回碼。(此時線程應(yīng)該出現(xiàn)非分離狀態(tài))。

在默認(rèn)情況下,線程的終止?fàn)顟B(tài)會保存到對該線程調(diào)用pthread_join(即默認(rèn)情況下線程處于非分離狀態(tài)),如果線程已經(jīng)處于分離狀態(tài),線程的底層存儲資源可以在線程終

止時立即被收回。當(dāng)線程被分離時,并不能用pthread_join函數(shù)等待它的終止?fàn)顟B(tài)。對分離狀態(tài)的線程進(jìn)行pthread_join的調(diào)用會產(chǎn)生失敗,返回EINVAL.

如果你壓根兒不關(guān)心一個線程的結(jié)束狀態(tài),那么也可以將一個線程設(shè)置為 detached 狀態(tài),從而來讓操作系統(tǒng)在該線程結(jié)束時來回收它所占的資源。將一個線程設(shè)置為

detached 狀態(tài)可以通過兩種方式來實現(xiàn)。

一種是調(diào)用 pthread_detach() 函數(shù),可以將線程 設(shè)置為 detached 狀態(tài)。

?pthread_detach:使線程進(jìn)入分離狀態(tài)。

#include <pthread.h> int pthread_detach(pthread_t tid);

返回值:若成功則返回0,否則返回錯誤編號。

另一種方法是在創(chuàng)建線程時就將它設(shè)置為 detached 狀態(tài),首先初始化一個線程屬性變量,然后將其設(shè)置為 detached 狀態(tài),最后將它作為參數(shù)傳入線程創(chuàng)建函數(shù) pthread_create(),這樣所創(chuàng)建出來的線程就直接處于 detached 狀態(tài)。方法如下代碼:

?

pthread_t tid;pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);pthread_create(&tid, &attr, THREAD_FUNCTION, arg);...pthread_attr_destroy(&attr);


總之為了在使用 Pthread 時 避免線程的資源在線程結(jié)束時不能得到正確釋放,從而避免產(chǎn)生潛在的內(nèi)存泄漏問題,在對待線程結(jié)束時,要確保該線程處于

?

detached狀態(tài),否則就需要調(diào)用 pthread_join() 函數(shù)來對其進(jìn)行資源回收

好了,說這么多,舉個例子先。

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <errno.h> #define THREADS 2 // 線程個數(shù)int num = 0; void *run(void *arg)// 線程執(zhí)行函數(shù),線程0執(zhí)行五次減,線程1執(zhí)行五次加 {int flag = (int)arg;if(flag %2) // 根據(jù)線程參數(shù)執(zhí)行相應(yīng)動作{for(int i = 0; i < 5; i++){num++;printf("running in thread %d, num: %d\n", flag, num);}}else{for(int i = 0; i < 5; i++){num--;printf("running in thread %d, num: %d\n", flag, num);}}return (void *)arg; } int main() {int i,err;void * ret;pthread_t tid[THREADS];for(i = 0; i < THREADS; i++) // 創(chuàng)建線程{if(0 != (err = pthread_create(&tid[i], NULL, run, (void *)i))){printf("thread create error %s\n",strerror(err));exit(-1);}}for(i = 0; i < THREADS; i++)阻塞等待線程,直到該線程退出 {if(0 != (err = pthread_join(tid[i], &ret))){printf("thread join error %s\n",strerror(err));exit(-1);}else{printf("thread %d exit code %d\n", i, (int)ret); // 打印線程退出代碼}}return 0; }

編譯 此程序別忘了加上加上-lpthread參數(shù)。pthread庫不是linux默認(rèn)的庫,所以在編譯時候需要指明libpthread.a庫。

g++? -o? thread_test ?thread_test.c? -lpthread

程序執(zhí)行結(jié)果:

兩個線程分別執(zhí)行自減 5次 自加 5次最后結(jié)果為0,我們可以看到 thread 1最后輸出為 0 結(jié)果是對的。但是仔細(xì)看所有的輸出,你會發(fā)現(xiàn)有異樣的東西。

thread 0 怎么輸出連續(xù)2個 -1,thread1 怎么輸出0然后直接 輸出-3 。原因是兩個線程可以同時操作 num 。 而且 num--(++),printf 不是一個原子操作 。

比如:當(dāng)thread0 執(zhí)行時 設(shè)此時num=-1 ,然后執(zhí)行 num--, 此時 num的值變?yōu)?-2 而此時還沒打印 printf? num的值 轉(zhuǎn)而線程thread執(zhí)行了num++,此時

num的值又變回 -1,所以會出現(xiàn)打印連續(xù)兩個 -1 。

?當(dāng)多個線程對共享區(qū)域進(jìn)行修改時,應(yīng)該采用同步的方式 才能達(dá)到我們有時候的需要,后面再敘述。


二,線程高級知識

1,線程屬性

?線程具有屬性,用pthread_attr_t表示,在對該結(jié)構(gòu)進(jìn)行處理之前必須進(jìn)行初始化,在使用后需要對其去除初始化。我們用pthread_attr_init函數(shù)對其初始化,用

?pthread_attr_destroy對其去除初始化。

線程屬性結(jié)構(gòu)如下:

typedef struct {int detachstate; //線程的分離狀態(tài)int schedpolicy; //線程調(diào)度策略struct sched_param schedparam; //線程的調(diào)度參數(shù)int inheritsched; //線程的繼承性int scope; //線程的作用域size_t guardsize; //線程棧末尾的警戒緩沖區(qū)大小int stackaddr_set;//堆棧地址集void * stackaddr; //線程棧的位置size_t stacksize; //線程棧的大小 }pthread_attr_t;


屬性值不能直接設(shè)置,須使用相關(guān)函數(shù)進(jìn)行操作,初始化的函數(shù)為pthread_attr_init,這個函數(shù)必須在pthread_create函數(shù)之前調(diào)用。屬性值不能直接設(shè)置,須使用相關(guān)函數(shù)進(jìn)行操作,初始化的函數(shù)為pthread_attr_init,這個函數(shù)必須在pthread_create函數(shù)之前調(diào)用。屬性值不能直接設(shè)置,每個屬性都對應(yīng)一些函數(shù)對其查看或修改。初始化的函數(shù)為pthread_attr_init,這個函數(shù)必須在pthread_create函數(shù)之前調(diào)用。

線程屬性修改函數(shù)介紹如下:

A,線程的分離狀態(tài)

線程的分離狀態(tài)決定一個線程以什么樣的方式來終止自己。在默認(rèn)情況下線程是非分離狀態(tài)的,這種情況下,只有當(dāng)pthread_join()函數(shù)返回時,

創(chuàng)建的線程才算終止,才能釋放自己占用的系統(tǒng)資源。而分離線程不是這樣子的,它沒有被其他的線程所等待,自己運(yùn)行結(jié)束了,線程也就終止了,

馬上釋放系統(tǒng)資源。

獲取/修改線程的分離狀態(tài)屬性:

int pthread_attr_getdetachstate(const pthread_attr_t * attr,int *detachstate); int pthread_attr_setdetachstate(pthread_attr_t *attr,int detachstate);

參數(shù):?attr?? 線程屬性變量 detachstate? 線程的分離狀態(tài)屬性 ,返回值:?若成功返回0,若失敗返回-1。

可以使用pthread_attr_setdetachstate函數(shù)把線程屬性detachstate設(shè)置為下面的兩個合法值之一:

設(shè)置為PTHREAD_CREATE_DETACHED,以分離狀態(tài)啟動線程;或者設(shè)置為PTHREAD_CREATE_JOINABLE,正常啟動線程。

可以使用pthread_attr_getdetachstate函數(shù)獲取當(dāng)前的datachstate線程屬性。

?

B,線程的繼承性

函數(shù)pthread_attr_setinheritsched和pthread_attr_getinheritsched分別用來設(shè)置和得到線程的繼承性,這兩個函數(shù)的定義如下:

#include <pthread.h> Int pthread_attr_getinheritsched(const pthread_attr_t *attr,int *inheritsched); int pthread_attr_setinheritsched(pthread_attr_t *attr,int inheritsched);

參數(shù):?attr 線程屬性變量 inheritsched?? 線程的繼承性? 返回值:若成功返回0,若失敗返回-1。

?這兩個函數(shù)具有兩個參數(shù),第1個是指向?qū)傩詫ο蟮闹羔?#xff0c;第2個是繼承性或指向繼承性的指針。繼承性決定調(diào)度的參數(shù)是從創(chuàng)建的進(jìn)程中繼承還是使用在

schedpolicy和schedparam屬性中顯式設(shè)置的調(diào)度信息。Pthreads不為inheritsched指定默認(rèn)值,因此如果你關(guān)心線程的調(diào)度策略和參數(shù),必須先設(shè)置該屬性。
繼承性的可能值是PTHREAD_INHERIT_SCHED(表示新線程將繼承創(chuàng)建線程的調(diào)度策略和參數(shù))和PTHREAD_EXPLICIT_SCHED(表示使用在schedpolicy

和schedparam屬性中顯式設(shè)置的調(diào)度策略和參數(shù))。如果你需要顯式的設(shè)置一個線程的調(diào)度策略或參數(shù),那么你必須在設(shè)置之前將inheritsched屬性設(shè)置為? THREAD_EXPLICIT_SCHED.

C,線程的調(diào)度策略

? 函數(shù)pthread_attr_setschedpolicy和pthread_attr_getschedpolicy分別用來設(shè)置和得到線程的調(diào)度策略。

#include <pthread.h> int pthread_attr_getschedpolicy(const pthread_attr_t *attr,int *policy); int pthread_attr_setschedpolicy(pthread_attr_t *attr,int policy);

參數(shù):attr? 線程屬性變量policy 調(diào)度策略返回值:若成功返回0,若失敗返回-1。

?這兩個函數(shù)具有兩個參數(shù),第1個參數(shù)是指向?qū)傩詫ο蟮闹羔?#xff0c;第2個參數(shù)是調(diào)度策略或指向調(diào)度策略的指針。調(diào)度策略可能的值是先進(jìn)先出(SCHED_FIFO)、

輪轉(zhuǎn)法(SCHED_RR),或其它(SCHED_OTHER)。SCHED_FIFO策略允許一個線程運(yùn)行直到有更高優(yōu)先級的線程準(zhǔn)備好,或者直到它自愿阻塞自己。
在SCHED_FIFO調(diào)度策略下,當(dāng)有一個線程準(zhǔn)備好時,除非有平等或更高優(yōu)先級的線程已經(jīng)在運(yùn)行,否則它會很快開始執(zhí)行。
SCHED_RR(輪循)策略是基本相同的,不同之處在于:如果有一個SCHED_RR策略的線程執(zhí)行了超過一個固定的時期(時間片間隔)沒有阻塞,而另外的SCHED_RR或SCHBD_FIPO策略的相同優(yōu)先級的線程準(zhǔn)備好時,運(yùn)行的線程將被搶占以便準(zhǔn)備好的線程可以執(zhí)行。
當(dāng)有SCHED_FIFO或SCHED_RR策賂的線程在一個條件變量上等持或等持加鎖同一個互斥量時,它們將以優(yōu)先級順序被喚醒。即,如果一個低優(yōu)先級的SCHED_FIFO

線程和一個高優(yōu)先織的SCHED_FIFO線程都在等待鎖相同的互斥且,則當(dāng)互斥量被解鎖時,高優(yōu)先級線程將總是被首先解除阻塞。

D,線程的調(diào)度參數(shù)

函數(shù)pthread_attr_getschedparam 和pthread_attr_setschedparam分別用來設(shè)置和得到線程的調(diào)度參數(shù)。

#include <pthread.h> int pthread_attr_getschedparam(const pthread_attr_t *attr,struct sched_param *param); int pthread_attr_setschedparam(pthread_attr_t *attr,const struct sched_param *param);

參數(shù):?attr 線程屬性變量 param?? sched_param結(jié)構(gòu) 返回值:?若成功返回0,若失敗返回-1。


這兩個函數(shù)具有兩個參數(shù),第1個參數(shù)是指向?qū)傩詫ο蟮闹羔?#xff0c;第2個參數(shù)是sched_param結(jié)構(gòu)或指向該結(jié)構(gòu)的指針。結(jié)構(gòu)sched_param在文件/usr/include /bits/sched.h中定義如下:

struct sched_param {int sched_priority; };


結(jié)構(gòu)sched_param的子成員sched_priority控制一個優(yōu)先權(quán)值,大的優(yōu)先權(quán)值對應(yīng)高的優(yōu)先權(quán)。系統(tǒng)支持的最大和最小優(yōu)先權(quán)值可以用sched_get_priority_max函數(shù)和sched_get_priority_min函數(shù)分別得到。結(jié)構(gòu)sched_param的子成員sched_priority控制一個優(yōu)先權(quán)值,大的優(yōu)先權(quán)值對應(yīng)高的優(yōu)先權(quán)。系統(tǒng)支持的最大和最小優(yōu)先權(quán)值可以用sched_get_priority_max函數(shù)和sched_get_priority_min函數(shù)分別得到。

注意:如果不是編寫實時程序,不建議修改線程的優(yōu)先級。因為,調(diào)度策略是一件非常復(fù)雜的事情,如果不正確使用會導(dǎo)致程序錯誤,從而導(dǎo)致死鎖等問題。如:在多線程應(yīng)用程序中為線程設(shè)置不同的優(yōu)先級別,有可能因為共享資源而導(dǎo)致優(yōu)先級倒置。

#include <pthread.h> int sched_get_priority_max(int policy); int sched_get_priority_min(int policy);

參數(shù):policy? 系統(tǒng)支持的線程優(yōu)先權(quán)的最大和最小值 返回值:若成功返回0,若失敗返回-1。

下面是有關(guān)線程屬性的程序例子:

?

#include<pthread.h> #include <unistd.h> #include <sched.h> #incude <stdio.h>void * child_thread(void *arg) {int policy;int max_priority,min_priority;struct sched_param param;pthread_attr_t attr;pthread_attr_init(&attr); /*初始化線程屬性變量*/pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED);/*設(shè)置線程繼承性*/pthread_attr_getinheritsched(&attr,&policy); /*獲得線程的繼承性*/if(policy==PTHREAD_EXPLICIT_SCHED)printf("Inheritsched:PTHREAD_EXPLICIT_SCHED\n");if(policy==PTHREAD_INHERIT_SCHED)printf("Inheritsched:PTHREAD_INHERIT_SCHED\n");pthread_attr_setschedpolicy(&attr,SCHED_RR);/*設(shè)置線程調(diào)度策略*/pthread_attr_getschedpolicy(&attr,&policy);/*取得線程的調(diào)度策略*/if(policy==SCHED_FIFO)printf("Schedpolicy:SCHED_FIFO\n");if(policy==SCHED_RR)printf("Schedpolicy:SCHED_RR\n");if(policy==SCHED_OTHER)printf("Schedpolicy:SCHED_OTHER\n");sched_get_priority_max(max_priority);/*獲得系統(tǒng)支持的線程優(yōu)先權(quán)的最大值*/sched_get_priority_min(min_priority);/* 獲得系統(tǒng)支持的線程優(yōu)先權(quán)的最小值*/printf("Max priority:%u\n",max_priority);printf("Min priority:%u\n",min_priority);param.sched_priority=max_priority;pthread_attr_setschedparam(&attr,?m);/*設(shè)置線程的調(diào)度參數(shù)*/printf("sched_priority:%u\n",param.sched_priority);/*獲得線程的調(diào)度參數(shù)*/pthread_attr_destroy(&attr);}int main( ){pthread_t child_thread_id;pthread_create(&child_thread_id,NULL,child_thread,NULL);pthread_join(child_thread_id,NULL);return 0; }


?

此外線程屬性還有如下,不在仔細(xì)敷述:
?線程的作用域? 函數(shù)pthread_attr_setscope和pthread_attr_getscope分別用來設(shè)置和得到線程的作用域
?線程堆棧的大小? 函數(shù)pthread_attr_setstacksize和pthread_attr_getstacksize分別用來設(shè)置和得到線程堆棧的大小
?線程堆棧的地址?? 函數(shù)pthread_attr_setstackaddr和pthread_attr_getstackaddr分別用來設(shè)置和得到線程堆棧的位置
?線程棧末尾的警戒緩沖區(qū)大小? 函數(shù)pthread_attr_getguardsize和pthread_attr_setguardsize分別用來設(shè)置和得到線程棧末尾的警戒緩沖區(qū)大小

?三,線程同步

線程的最大特點(diǎn)是資源的共享性,但資源共享中的同步問題是多線程編程的難點(diǎn)。linux下提供了多種方式來處理線程同步,最常用的是互斥鎖、條件變量和異步信號。?

1,互斥鎖

?什么是互斥鎖

?

一種在多線程程序中同步訪問手段是使用互斥量。程序員給某個對象加上一把“鎖”,每次只允許一個線程去訪問它。如果想對代碼關(guān)鍵部分的訪問進(jìn)行控制,你必須在

進(jìn)入這段代碼之前鎖定一把互斥量,在完成操作之后再打開它。

互斥鎖函數(shù)有:

?

? pthread_mutex_init 初始化一個互斥量pthread_mutex_lock 給一個互斥量加鎖pthread_mutex_trylock 加鎖,如果失敗不阻塞 pthread_mutex_destroy 銷毀一個互斥量pthread_mutex_unlock 解鎖


?? 可以通過使用pthread的互斥接口保護(hù)數(shù)據(jù),確保同一時間只有一個線程訪問數(shù)據(jù)。互斥量從本質(zhì)上說是一把鎖,在訪問共享資源前對互斥量進(jìn)行加鎖,在訪問完成后

?

?釋放互斥量上的鎖。對互斥量進(jìn)行加鎖以后,任何其他試圖再次對互斥量加鎖的線程將會被阻塞直到當(dāng)前線程釋放該互斥鎖。如果釋放互斥鎖時有多個線程阻塞,所以

在該互斥鎖上的阻塞線程都會變成可進(jìn)行狀態(tài),第一個變成運(yùn)行狀態(tài)的線程可以對互斥量加鎖,其他線程在次被阻塞,等待下次運(yùn)行狀態(tài)。

互斥量用pthread_mutex_t數(shù)據(jù)類型來表示

(1) 在使用互斥量以前,必須首先對它進(jìn)行初始化pthread_mutex_init()或靜態(tài)賦值pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIER

?

#include <pthread.h> int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutex_t *attr);

參數(shù):mutex? 互斥量 attr???? 互斥鎖屬性 返回值:若成功則返回0,否則返回錯誤編號。
?mutex 是我們要鎖住的互斥量, attr 是互斥鎖的屬性,可用相應(yīng)的函數(shù)修改:

?

attr_t有:
????? PTHREAD_MUTEX_TIMED_NP:其余線程等待隊列
????? PTHREAD_MUTEX_RECURSIVE_NP:嵌套鎖,允許線程多次加鎖,不同線程,解鎖后重新競爭
????? PTHREAD_MUTEX_ERRORCHECK_NP:檢錯,與一同,線程請求已用鎖,返回EDEADLK;
????? PTHREAD_MUTEX_ADAPTIVE_NP:適應(yīng)鎖,解鎖后重新競爭
(2) 對互斥量加減鎖

????? 對互斥量加/減鎖:

?

#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);

返回值:若成功則返回0,否則返回錯誤編號。

?

對互斥量進(jìn)行加鎖,需要調(diào)用pthread_mutex_lock,如果互斥量已經(jīng)上鎖,調(diào)用線程阻塞直至互斥量解鎖

對互斥量解鎖,需要調(diào)用pthread_mutex_unlock.

如果線程不希望被阻塞,他可以使用pthread_mutex_trylock嘗試對互斥量進(jìn)行加鎖。如果調(diào)用pthread_mutex_trylock時互斥量處于未鎖住狀態(tài),

那么pthread_mutex_trylock將鎖住互斥量,否則就會失敗,不能鎖住互斥量,而返回EBUSY

(3)清除鎖,destroy(此時鎖必需unlock,否則返回EBUSY,//Linux下互斥鎖不占用資源內(nèi)存
????? 釋放對互斥變量分配的資源:

?

#include <pthread.h> int pthread_mutex_destroy(pthread_mutex_t *mutex);

返回值:若成功則返回0,否則返回錯誤編號。

好了,既然了解了互斥鎖那么現(xiàn)在我們就 把 最開始的那個多線程的例子進(jìn)行改裝:

只需更改線程子函數(shù),代碼如下:

?

void *run(void *arg) {int flag = (int)arg;if(flag %2){for(int i = 0; i < 5; i++){pthread_mutex_lock(&mylock);// 加鎖num++;printf("running in thread %d, num: %d\n", flag, num);pthread_mutex_unlock(&mylock);// 解鎖}}else{for(int i = 0; i < 5; i++){pthread_mutex_lock(&mylock);// 加鎖num--;printf("running in thread %d, num: %d\n", flag, num);pthread_mutex_unlock(&mylock);// 解鎖}}return (void *)arg; }

?

執(zhí)行結(jié)果如下:

?

可以和先前的那個進(jìn)行相應(yīng)的比較,發(fā)現(xiàn)是不是用互斥量后程序正常了!


應(yīng)用互斥量需要注意的幾點(diǎn):
?1、互斥量需要時間來加鎖和解鎖。鎖住較少互斥量的程序通常運(yùn)行得更快。所以,互斥量應(yīng)該盡量少,夠用即可,每個互斥量保護(hù)的區(qū)域應(yīng)則盡量大。

? 2、互斥量的本質(zhì)是串行執(zhí)行。如果很多線程需要領(lǐng)繁地加鎖同一個互斥量,則線程的大部分時間就會在等待,這對性能是有害的。如果互斥量保護(hù)的

? 數(shù)據(jù)(或代碼)包含彼此無關(guān)的片段,則可以特大的互斥量分解為幾個小的互斥量來提高性能。這樣,任意時刻需要小互斥量的線程減少,線程等待時間

? 就會減少。所以,互斥量應(yīng)該足夠多(到有意義的地步),每個互斥量保護(hù)的區(qū)域則應(yīng)盡量的少。


2,條件變量(cond)

?什么是條件變量?

? 與互斥鎖不同,條件變量是用來等待而不是用來上鎖的。條件變量用來自動阻塞一個線程,直到某特殊情況發(fā)生為止。通常條件變量和互斥鎖同時使用。

? 條件變量使我們可以睡眠等待某種條件出現(xiàn)。條件變量是利用線程間共享的全局變量進(jìn)行同步的一種機(jī)制,主要包括兩個動作

? 一個線程等待"條件變量的條件成立"而掛起;另一個線程使"條件成立"(給出條件成立信號)。
條件的檢測是在互斥鎖的保護(hù)下進(jìn)行的如果一個條件為假,一個線程自動阻塞,并釋放等待狀態(tài)改變的互斥鎖。如果另一個線程改變了條件,它發(fā)信號

給關(guān)聯(lián)的條件變量,喚醒一個或多個等待它的線程,重新獲得互斥鎖,重新評價條件。如果兩進(jìn)程共享可讀寫的內(nèi)存,條件變量可以被用來實現(xiàn)這兩進(jìn)程

間的線程同步。

利用線程間共享的全局變量進(jìn)行同步的一種機(jī)制。

int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);// 條件變量初始化 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);int pthread_cond_destroy(pthread_cond_t *cond); // 條件變量摧毀int pthread_cond_signal(pthread_cond_t *cond);//在相同條件變量上阻塞的線程將被解鎖,如果同時有多個線程阻塞,則由調(diào)度策略確定接收通知的線程int pthread_cond_broadcast(pthread_cond_t *cond); // 解除所有線程的阻塞

(1)初始化.init()或者pthread_cond_t cond=PTHREAD_COND_INITIALIER;屬性置為NULL

?

(2)等待條件成立.pthread_wait,pthread_timewait.wait()釋放鎖,并阻塞等待條件變量為真 timewait()設(shè)置等待時間,仍未signal,返回ETIMEOUT(加鎖保證只有一個線程wait)

(3)激活條件變量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待線程)

(4)清除條件變量:destroy;無線程等待,否則返回EBUSY

注意: 條件變量無論哪種等待方式,都必須和一個互斥鎖配合,以防止多個線程同時請求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的競爭條件

(Race Condition).mutex互斥鎖必須是普通鎖(PTHREAD_MUTEX_TIMED_NP)或者適應(yīng)鎖(PTHREAD_MUTEX_ADAPTIVE_NP),且在調(diào)用pthread_cond_wait()

?前必須由本線程加鎖(pthread_mutex_lock()),而在更新條件等待隊列以前,mutex保持鎖定狀態(tài),并在線程掛起進(jìn)入等待前解鎖在條件滿足從而離開pthread_cond_wait()之前,mutex將被重新加鎖,以與進(jìn)入pthread_cond_wait()前的加鎖動作對應(yīng)。

舉個例子先,利用2線程產(chǎn)生擁有連續(xù)10個AB的序列。

代碼如下:

?

#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #define DE 1 // 是否debug #define THREADS 2 // 線程個數(shù)int num = 0; pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;//互斥量 pthread_cond_t ready = PTHREAD_COND_INITIALIZER;//條件變量 void debug_p(char *str, int i) {#ifdef DE // 如果定義了debug則輸出 調(diào)試信息printf("\nthread %d %s\n", i, str); #endif } void *my_start(void *arg)// {int id = (int)arg;char c = 'A' + id;int ret = -1,i = 0;for(; i < 10; i++){pthread_mutex_lock(&mylock);//調(diào)用pthread_cond_wait前,必須獲得互斥鎖while(id != num){debug_p("waitting", id);ret = pthread_cond_wait(&ready, &mylock);//把線程放入等待條件的線程列表,然后對互斥鎖進(jìn)行解鎖,這兩部都是原子操作。pthread_cond_wait返回時,互斥量再次鎖住。 if(ret == 0)debug_p("wait success", id);elsedebug_p("wait failed", id);}printf("%c ", c);num = ++num%THREADS;pthread_mutex_unlock(&mylock);pthread_cond_broadcast(&ready);//喚醒等待該條件的所有線程 }return (void *)0; }int main(int argc, char* argv[]) {int i;pthread_t tid[THREADS];void *ret;for(i = 0; i < THREADS; i++){if(pthread_create(&tid[i], 0, my_start ,(void *)i) != 0){printf("threted create error!\n");exit(-1);}}for(i = 0; i < THREADS; i++){if(pthread_join(tid[i], &ret) != 0){printf("threted join error!\n");exit(-1);}}printf("\n");return 0; }


可以看到,在當(dāng) #define 1 開啟,即debug輸出我們自定義的調(diào)試信息時,thread 0 剛開始滿足條件輸出A,num加1 解鎖互斥量,

?

此時無等待該條件的線程。此時num=1 ,id=0 進(jìn)入while循環(huán) 顯然thread 0進(jìn)入等待狀態(tài),然后執(zhí)行thread 1 即此時 id=1,而此時

num也為1 此時跳過while循環(huán)輸出 B。此時num++, thread 1進(jìn)入 waititng,然后解鎖互斥量,最后喚醒等待該條件的線程thread 0。

輸出 A ....

注釋掉// #define DE 即可輸出 :ABABABABABABABABABAB


3, 信號量

如同進(jìn)程一樣,線程也可以通過信號量來實現(xiàn)通信,雖然是輕量級的。

信號量函數(shù)的名字都以"sem_"打頭。線程使用的基本信號量函數(shù)有四個。
#include <semaphore.h>
int sem_init (sem_t *sem , int pshared, unsigned int value);
這是對由sem指定的信號量進(jìn)行初始化,設(shè)置好它的共享選項(linux 只支持為0,即表示它是當(dāng)前進(jìn)程的局部信號量),然后給它一個初始值VALUE。

兩個原子操作函數(shù):
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);
這兩個函數(shù)都要用一個由sem_init調(diào)用初始化的信號量對象的指針做參數(shù)。
sem_post:給信號量的值加1;
sem_wait:給信號量減1;對一個值為0的信號量調(diào)用sem_wait,這個函數(shù)將會等待直到有其它線程使它不再是0為止。

int sem_destroy(sem_t *sem);
這個函數(shù)的作用是再我們用完信號量后都它進(jìn)行清理。歸還自己占有的一切資源。


?

條件變量與互斥鎖、信號量的區(qū)別:

?? 到這里,我們把posix的互斥鎖、信號量、條件變量都接受完了,下面我們來比較一下他們。
?? a.互斥鎖必須總是由給它上鎖的線程解鎖,信號量的掛出即不必由執(zhí)行過它的等待操作的同一進(jìn)程執(zhí)行。一個線程可以等待某個給定信號燈,

?????? 而另一個線程可以掛出該信號燈。

??? b.互斥鎖要么鎖住,要么被解開(二值狀態(tài),類型二值信號量)。
??? c.由于信號量有一個與之關(guān)聯(lián)的狀態(tài)(它的計數(shù)值),信號量掛出操作總是被記住。然而當(dāng)向一個條件變量發(fā)送信號時,如果沒有線程等待在

???????? 該條件變量上,那么該信號將丟失。
??? d.互斥鎖是為了上鎖而優(yōu)化的,條件變量是為了等待而優(yōu)化的,信號燈即可用于上鎖,也可用于等待,因而可能導(dǎo)致更多的開銷和更高的復(fù)雜性。


待續(xù)...


參考資料:

<APUE>

<UNP,第二卷>

http://blog.csdn.net/lanyan822/article/details/7587972

?

總結(jié)

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

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