加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
    • 9.2  Linux線程編程
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

多線程編程之:Linux線程編程

2013/09/13
1
閱讀需 54 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

?

9.2? Linux線程編程

9.2.1? 線程基本編程

這里要講的線程相關(guān)操作都是用戶空間中的線程的操作。在Linux中,一般pthread線程庫是一套通用的線程庫,是由POSIX提出的,因此具有很好的可移植性。

(1)函數(shù)說明。

創(chuàng)建線程實(shí)際上就是確定調(diào)用該線程函數(shù)的入口點(diǎn),這里通常使用的函數(shù)是pthread_create()。在線程創(chuàng)建以后,就開始運(yùn)行相關(guān)的線程函數(shù),在該函數(shù)運(yùn)行完之后,該線程也就退出了,這也是線程退出一種方法。另一種退出線程的方法是使用函數(shù)pthread_exit(),這是線程的主動(dòng)行為。這里要注意的是,在使用線程函數(shù)時(shí),不能隨意使用exit()退出函數(shù)進(jìn)行出錯(cuò)處理,由于exit()的作用是使調(diào)用進(jìn)程終止,往往一個(gè)進(jìn)程包含多個(gè)線程,因此,在使用exit()之后,該進(jìn)程中的所有線程都終止了。因此,在線程中就可以使用pthread_exit()來代替進(jìn)程中的exit()。

由于一個(gè)進(jìn)程中的多個(gè)線程是共享數(shù)據(jù)段的,因此通常在線程退出之后,退出線程所占用的資源并不會(huì)隨著線程的終止而得到釋放。正如進(jìn)程之間可以用wait()系統(tǒng)調(diào)用來同步終止并釋放資源一樣,線程之間也有類似機(jī)制,那就是pthread_join()函數(shù)。pthread_join()可以用于將當(dāng)前線程掛起來等待線程的結(jié)束。這個(gè)函數(shù)是一個(gè)線程阻塞的函數(shù),調(diào)用它的函數(shù)將一直等待到被等待的線程結(jié)束為止,當(dāng)函數(shù)返回時(shí),被等待線程的資源就被收回。

前面已提到線程調(diào)用pthread_exit()函數(shù)主動(dòng)終止自身線程。但是在很多線程應(yīng)用中,經(jīng)常會(huì)遇到在別的線程中要終止另一個(gè)線程的執(zhí)行的問題。此時(shí)調(diào)用pthread_cancel()函數(shù)實(shí)現(xiàn)這種功能,但在被取消的線程的內(nèi)部需要調(diào)用pthread_setcancel()函數(shù)和pthread_setcanceltype()函數(shù)設(shè)置自己的取消狀態(tài),例如被取消的線程接收到另一個(gè)線程的取消請(qǐng)求之后,是接受還是忽略這個(gè)請(qǐng)求;如果接受,是立刻進(jìn)行終止操作還是等待某個(gè)函數(shù)的調(diào)用等。

(2)函數(shù)格式。

表9.1列出了pthread_create()函數(shù)的語法要點(diǎn)。

表9.1??????????????????????????????????????????? pthread_create()函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

int pthread_create ((pthread_t *thread, pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg))

函數(shù)傳入值

thread:線程標(biāo)識(shí)符

attr:線程屬性設(shè)置(其具體設(shè)置參見9.2.3小節(jié)),通常取為NULL

start_routine:線程函數(shù)的起始地址,是一個(gè)以指向void的指針作為參數(shù)和返回值的函數(shù)指針

arg:傳遞給start_routine的參數(shù)

函數(shù)返回值

成功:0

出錯(cuò):返回錯(cuò)誤碼

表9.2列出了pthread_exit()函數(shù)的語法要點(diǎn)。

表9.2????????????????????????????????????????????? pthread_exit()函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

void pthread_exit(void *retval)

函數(shù)傳入值

retval:線程結(jié)束時(shí)的返回值,可由其他函數(shù)如pthread_join()來獲取

表9.3列出了pthread_join()函數(shù)的語法要點(diǎn)。

表9.3????????????????????????????????????????????? pthread_join()函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

int pthread_join ((pthread_t th, void **thread_return))

函數(shù)傳入值

th:等待線程的標(biāo)識(shí)符

thread_return:用戶定義的指針,用來存儲(chǔ)被等待線程結(jié)束時(shí)的返回值(不為NULL時(shí))

函數(shù)返回值

成功:0

出錯(cuò):返回錯(cuò)誤碼

表9.4列出了pthread_cancel()函數(shù)的語法要點(diǎn)。

表9.4??????????????????????????????????????????? pthread_cancel()函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

int pthread_cancel((pthread_t th)

函數(shù)傳入值

th:要取消的線程的標(biāo)識(shí)符

函數(shù)返回值

成功:0

出錯(cuò):返回錯(cuò)誤碼

?

(3)函數(shù)使用。

以下實(shí)例中創(chuàng)建了3個(gè)線程,為了更好地描述線程之間的并行執(zhí)行,讓3個(gè)線程重用同一個(gè)執(zhí)行函數(shù)。每個(gè)線程都有5次循環(huán)(可以看成5個(gè)小任務(wù)),每次循環(huán)之間會(huì)隨機(jī)等待1~10s的時(shí)間,意義在于模擬每個(gè)任務(wù)的到達(dá)時(shí)間是隨機(jī)的,并沒有任何特定規(guī)律。

/* thread.c */

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#define THREAD_NUMBER?????? 3???????????????? /*線程數(shù)*/

#define REPEAT_NUMBER?????? 5???????????????? /*每個(gè)線程中的小任務(wù)數(shù)*/

#define DELAY_TIME_LEVELS? 10.0???????????? /*小任務(wù)之間的最大時(shí)間間隔*/

void *thrd_func(void *arg)

{ /* 線程函數(shù)例程 */

???? int thrd_num = (int)arg;

???? int delay_time = 0;

???? int count = 0;

????

???? printf("Thread %d is startingn", thrd_num);

???? for (count = 0; count < REPEAT_NUMBER; count++)

???? {

????????? delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;

????????? sleep(delay_time);

????????? printf("tThread %d: job %d delay = %dn",

????????????????????????????? thrd_num, count, delay_time);

???? }

???? printf("Thread %d finishedn", thrd_num);

???? pthread_exit(NULL);

}

int main(void)

{

?? ??pthread_t thread[THREAD_NUMBER];

?? ??int no = 0, res;

???? void * thrd_ret;

????

???? srand(time(NULL));

????

???? for (no = 0; no < THREAD_NUMBER; no++)

???? {

????????? /* 創(chuàng)建多線程 */

????????? res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);

????????? if (res != 0)

???????? ?{

?????????????? printf("Create thread %d failedn", no);

?????????????? exit(res);

????????? }

???? }

????

???? printf("Create treads successn Waiting for threads to finish...n");

???? for (no = 0; no < THREAD_NUMBER; no++)

???? {

????????? /* 等待線程結(jié)束 */

???????? ?res = pthread_join(thread[no], &thrd_ret);

????????? if (!res)

????????? {

????????? printf("Thread %d joinedn", no);

????????? }

????????? else

????????? {

????????? printf("Thread %d join failedn", no);

????????? }

???? }

???? return 0;?? ?????

}

以下是程序運(yùn)行結(jié)果。可以看出每個(gè)線程的運(yùn)行和結(jié)束是獨(dú)立與并行的。

?$ ./thread

Create treads success

Waiting for threads to finish...

Thread 0 is starting

Thread 1 is starting

Thread 2 is starting

??????? Thread 1: job 0 delay = 6

??????? Thread 2: job 0 delay = 6

??????? Thread 0: job 0 delay = 9

??????? Thread 1: job 1 delay = 6

??????? Thread 2: job 1 delay = 8

??????? Thread 0: job 1 delay = 8

??????? Thread 2: job 2 delay = 3

??????? Thread 0: job 2 delay = 3

??????? Thread 2: job 3 delay = 3

??????? Thread 2: job 4 delay = 1

Thread 2 finished

??????? Thread 1: job 2 delay = 10

??????? Thread 1: job 3 delay = 4

??????? Thread 1: job 4 delay = 1

Thread 1 finished

??????? Thread 0: job 3 delay = 9

??????? Thread 0: job 4 delay = 2

Thread 0 finished

Thread 0 joined

Thread 1 joined

Thread 2 joined

9.2.2? 線程之間的同步與互斥

由于線程共享進(jìn)程的資源和地址空間,因此在對(duì)這些資源進(jìn)行操作時(shí),必須考慮到線程間資源訪問的同步與互斥問題。這里主要介紹POSIX中兩種線程同步機(jī)制,分別為互斥鎖和信號(hào)量。這兩個(gè)同步機(jī)制可以互相通過調(diào)用對(duì)方來實(shí)現(xiàn),但互斥鎖更適合用于同時(shí)可用的資源是惟一的情況;信號(hào)量更適合用于同時(shí)可用的資源為多個(gè)的情況。

1.互斥鎖線程控制

(1)函數(shù)說明。

互斥鎖是用一種簡(jiǎn)單的加鎖方法來控制對(duì)共享資源的原子操作。這個(gè)互斥鎖只有兩種狀態(tài),也就是上鎖和解鎖,可以把互斥鎖看作某種意義上的全局變量。在同一時(shí)刻只能有一個(gè)線程掌握某個(gè)互斥鎖,擁有上鎖狀態(tài)的線程能夠?qū)蚕碣Y源進(jìn)行操作。若其他線程希望上鎖一個(gè)已經(jīng)被上鎖的互斥鎖,則該線程就會(huì)掛起,直到上鎖的線程釋放掉互斥鎖為止??梢哉f,這把互斥鎖保證讓每個(gè)線程對(duì)共享資源按順序進(jìn)行原子操作。

互斥鎖機(jī)制主要包括下面的基本函數(shù)。

n???? 互斥鎖初始化:pthread_mutex_init()

n???? 互斥鎖上鎖:pthread_mutex_lock()

n???? 互斥鎖判斷上鎖:pthread_mutex_trylock()

n???? 互斥鎖接鎖:pthread_mutex_unlock()

n???? 消除互斥鎖:pthread_mutex_destroy()

其中,互斥鎖可以分為快速互斥鎖、遞歸互斥鎖和檢錯(cuò)互斥鎖。這3種鎖的區(qū)別主要在于其他未占有互斥鎖的線程在希望得到互斥鎖時(shí)是否需要阻塞等待??焖冁i是指調(diào)用線程會(huì)阻塞直至擁有互斥鎖的線程解鎖為止。遞歸互斥鎖能夠成功地返回,并且增加調(diào)用線程在互斥上加鎖的次數(shù),而檢錯(cuò)互斥鎖則為快速互斥鎖的非阻塞版本,它會(huì)立即返回并返回一個(gè)錯(cuò)誤信息。默認(rèn)屬性為快速互斥鎖。

(2)函數(shù)格式。

表9.5列出了pthread_mutex_init()函數(shù)的語法要點(diǎn)。

表9.5?????????????????????????????????????? pthread_mutex_init()函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)

函數(shù)傳入值

mutex:互斥鎖

Mutexattr

PTHREAD_MUTEX_INITIALIZER:創(chuàng)建快速互斥鎖

PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP:創(chuàng)建遞歸互斥鎖

PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP:創(chuàng)建檢錯(cuò)互斥鎖

函數(shù)返回值

成功:0

出錯(cuò):返回錯(cuò)誤碼

表9.6列出了pthread_mutex_lock()等函數(shù)的語法要點(diǎn)。

表9.6??????????????????????????????????? pthread_mutex_lock()等函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

int pthread_mutex_lock(pthread_mutex_t *mutex,)
int pthread_mutex_trylock(pthread_mutex_t *mutex,)
int pthread_mutex_unlock(pthread_mutex_t *mutex,)
int pthread_mutex_destroy(pthread_mutex_t *mutex,)

函數(shù)傳入值

mutex:互斥鎖

函數(shù)返回值

成功:0

出錯(cuò):-1

?

(3)使用實(shí)例。

下面的實(shí)例是在9.2.1小節(jié)示例代碼的基礎(chǔ)上增加互斥鎖功能,實(shí)現(xiàn)原本獨(dú)立與無序的多個(gè)線程能夠按順序執(zhí)行。

/*thread_mutex.c*/

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#define THREAD_NUMBER??????? 3??????????? /* 線程數(shù) */

#define REPEAT_NUMBER??????? 3??????????? /* 每個(gè)線程的小任務(wù)數(shù) */

#define DELAY_TIME_LEVELS 10.0???????? /*小任務(wù)之間的最大時(shí)間間隔*/

pthread_mutex_t mutex;

void *thrd_func(void *arg)

{

???? int thrd_num = (int)arg;

???? int delay_time = 0, count = 0;

???? int res;

???? /* 互斥鎖上鎖 */

???? res = pthread_mutex_lock(&mutex);

???? if (res)

???? {

????????? printf("Thread %d lock failedn", thrd_num);

????? ????pthread_exit(NULL);

???? }

???? printf("Thread %d is startingn", thrd_num);

???? for (count = 0; count < REPEAT_NUMBER; count++)

???? {?????????

???? delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;

???? sleep(delay_time);

???? printf("tThread %d: job %d delay = %dn",

????????????????????????????????? thrd_num, count, delay_time);

???? }

???? printf("Thread %d finishedn", thrd_num);

???? pthread_exit(NULL);

}

int main(void)

{

???? pthread_t thread[THREAD_NUMBER];

???? int no = 0, res;

???? void * thrd_ret;

????

???? srand(time(NULL));

???? /* 互斥鎖初始化 */

???? pthread_mutex_init(&mutex, NULL);

???? for (no = 0; no < THREAD_NUMBER; no++)

???? {

????????? res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);

????????? if (res != 0)

????????? {

????????????? printf("Create thread %d failedn", no);

????????????? exit(res);

????????? }

???? }????

???? printf("Create treads successn Waiting for threads to finish...n");

???? for (no = 0; no < THREAD_NUMBER; no++)

???? {

????????? res = pthread_join(thread[no], &thrd_ret);

????????? if (!res)

????????? {

????????? printf("Thread %d joinedn", no);

????????? }

????????? else

????????? {

????????????? printf("Thread %d join failedn", no);

????????? }

????????? /* 互斥鎖解鎖 */

????????? pthread_mutex_unlock(&mutex);

???? }????

???? pthread_mutex_destroy(&mutex);?????????

???? return 0;???????

}

該實(shí)例的運(yùn)行結(jié)果如下所示。這里3個(gè)線程之間的運(yùn)行順序跟創(chuàng)建線程的順序相同。

$ ./thread_mutex

Create treads success

?Waiting for threads to finish...

Thread 0 is starting

??????? Thread 0: job 0 delay = 7

??????? Thread 0: job 1 delay = 7

??????? Thread 0: job 2 delay = 6

Thread 0 finished

Thread 0 joined

Thread 1 is starting

??????? Thread 1: job 0 delay = 3

??????? Thread 1: job 1 delay = 5

??????? Thread 1: job 2 delay = 10

Thread 1 finished

Thread 1 joined

Thread 2 is starting

??????? Thread 2: job 0 delay = 6

??????? Thread 2: job 1 delay = 10

??????? Thread 2: job 2 delay = 8

Thread 2 finished

Thread 2 joined

?

2.信號(hào)量線程控制

(1)信號(hào)量說明。

在第8章中已經(jīng)講到,信號(hào)量也就是操作系統(tǒng)中所用到的PV原子操作,它廣泛用于進(jìn)程或線程間的同步與互斥。信號(hào)量本質(zhì)上是一個(gè)非負(fù)的整數(shù)計(jì)數(shù)器,它被用來控制對(duì)公共資源的訪問。這里先來簡(jiǎn)單復(fù)習(xí)一下PV原子操作的工作原理。

PV原子操作是對(duì)整數(shù)計(jì)數(shù)器信號(hào)量sem的操作。一次P操作使sem減一,而一次V操作使sem加一。進(jìn)程(或線程)根據(jù)信號(hào)量的值來判斷是否對(duì)公共資源具有訪問權(quán)限。當(dāng)信號(hào)量sem的值大于等于零時(shí),該進(jìn)程(或線程)具有公共資源的訪問權(quán)限;相反,當(dāng)信號(hào)量sem的值小于零時(shí),該進(jìn)程(或線程)就將阻塞直到信號(hào)量sem的值大于等于0為止。

PV原子操作主要用于進(jìn)程或線程間的同步和互斥這兩種典型情況。若用于互斥,幾個(gè)進(jìn)程(或線程)往往只設(shè)置一個(gè)信號(hào)量sem,它們的操作流程如圖9.2所示。

當(dāng)信號(hào)量用于同步操作時(shí),往往會(huì)設(shè)置多個(gè)信號(hào)量,并安排不同的初始值來實(shí)現(xiàn)它們之間的順序執(zhí)行,它們的操作流程如圖9.3所示。

???????????????

圖9.2? 信號(hào)量互斥操作??????????????????????????????? 圖9.3? 信號(hào)量同步操作

(2)函數(shù)說明。

Linux實(shí)現(xiàn)了POSIX的無名信號(hào)量,主要用于線程間的互斥與同步。這里主要介紹幾個(gè)常見函數(shù)。

n???? sem_init()用于創(chuàng)建一個(gè)信號(hào)量,并初始化它的值。

n???? sem_wait()和sem_trywait()都相當(dāng)于P操作,在信號(hào)量大于零時(shí)它們都能將信號(hào)量的值減一,兩者的區(qū)別在于若信號(hào)量小于零時(shí),sem_wait()將會(huì)阻塞進(jìn)程,而sem_trywait()則會(huì)立即返回。

n???? sem_post()相當(dāng)于V操作,它將信號(hào)量的值加一同時(shí)發(fā)出信號(hào)來喚醒等待的進(jìn)程。

n???? sem_getvalue()用于得到信號(hào)量的值。

n???? sem_destroy()用于刪除信號(hào)量。

(3)函數(shù)格式。

表9.7列出了sem_init()函數(shù)的語法要點(diǎn)。

表9.7????????????????????????????????????????????????? sem_init()函數(shù)語法要點(diǎn)

所需頭文件

#include <semaphore.h>

函數(shù)原型

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

函數(shù)傳入值

sem:信號(hào)量指針

pshared:決定信號(hào)量能否在幾個(gè)進(jìn)程間共享。由于目前Linux還沒有實(shí)現(xiàn)進(jìn)程間共享信號(hào)量,所以這個(gè)值只能夠取0,就表示這個(gè)信號(hào)量是當(dāng)前進(jìn)程的局部信號(hào)量

value:信號(hào)量初始化值

函數(shù)返回值

成功:0

出錯(cuò):-1

表9.8列出了sem_wait()等函數(shù)的語法要點(diǎn)。

表9.8?????????????????????????????????????????????? sem_wait()等函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

int sem_wait(sem_t *sem)
int sem_trywait(sem_t *sem)
int sem_post(sem_t *sem)
int sem_getvalue(sem_t *sem)
int sem_destroy(sem_t *sem)

函數(shù)傳入值

sem:信號(hào)量指針

函數(shù)返回值

成功:0

出錯(cuò):-1

?

(4)使用實(shí)例。

在前面已經(jīng)通過互斥鎖同步機(jī)制實(shí)現(xiàn)了多線程的順序執(zhí)行。下面的例子是用信號(hào)量同步機(jī)制實(shí)現(xiàn)3個(gè)線程之間的有序執(zhí)行,只是執(zhí)行順序是跟創(chuàng)建線程的順序相反。

/*thread_sem.c*/

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#include <semaphore.h>

#define THREAD_NUMBER??????? 3???????????????? /* 線程數(shù) */

#define REPEAT_NUMBER??????? 3???????????????? /* 每個(gè)線程中的小任務(wù)數(shù) */

#define DELAY_TIME_LEVELS?? 10.0???????????? /*小任務(wù)之間的最大時(shí)間間隔*/

sem_t sem[THREAD_NUMBER];

void *thrd_func(void *arg)

{

???? int thrd_num = (int)arg;

???? int delay_time = 0;

???? int count = 0;

???? /* 進(jìn)行P操作 */

???? sem_wait(&sem[thrd_num]);

???? printf("Thread %d is startingn", thrd_num);

????

???? for (count = 0; count < REPEAT_NUMBER; count++)

? ???{

????????? delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;

????????? sleep(delay_time);

????????? printf("tThread %d: job %d delay = %dn",

thrd_num, count, delay_time);

???? }

???? printf("Thread %d finishedn", thrd_num);

???? pthread_exit(NULL);

}

int main(void)

{

???? pthread_t thread[THREAD_NUMBER];

???? int no = 0, res;

???? void * thrd_ret;

????

???? srand(time(NULL));

???? for (no = 0; no < THREAD_NUMBER; no++)

???? {

????????? sem_init(&sem[no], 0, 0);

????????? res = pthread_create(&thread[no], NULL, thrd_func, (void*)no);

????????? if (res != 0)

????????? {

????????????? printf("Create thread %d failedn", no);

????????????? exit(res);

????????? }

???? }

????

???? printf("Create treads successn Waiting for threads to finish...n");

???? /* 對(duì)最后創(chuàng)建的線程的信號(hào)量進(jìn)行V操作 */

???? sem_post(&sem[THREAD_NUMBER - 1]);

???? for (no = THREAD_NUMBER - 1; no >= 0; no--)

???? {

???????? res = pthread_join(thread[no], &thrd_ret);

???????? if (!res)

???????? {

??????????? printf("Thread %d joinedn", no);

????????? }

????????? else

????????? {

???????????? printf("Thread %d join failedn", no);

????????? }

????????? /* 進(jìn)行V操作 */

????????? sem_post(&sem[(no + THREAD_NUMBER - 1) % THREAD_NUMBER]);

???? }

????

???? for (no = 0; no < THREAD_NUMBER; no++)

???? {

????????? /* 刪除信號(hào)量 */

????????? sem_destroy(&sem[no]);

???? }

?????????

???? return 0;???????

}

?

該程序運(yùn)行結(jié)果如下所示:

$ ./thread_sem

Create treads success

Waiting for threads to finish...

Thread 2 is starting

??????? Thread 2: job 0 delay = 9

??????? Thread 2: job 1 delay = 5

??????? Thread 2: job 2 delay = 10

Thread 2 finished

Thread 2 joined

Thread 1 is starting

??????? Thread 1: job 0 delay = 7

??????? Thread 1: job 1 delay = 4

??????? Thread 1: job 2 delay = 4

Thread 1 finished

Thread 1 joined

Thread 0 is starting

?? ?????Thread 0: job 0 delay = 10

??????? Thread 0: job 1 delay = 8

??????? Thread 0: job 2 delay = 9

Thread 0 finished

Thread 0 joined

9.2.3? 線程屬性

(1)函數(shù)說明。

pthread_create()函數(shù)的第二個(gè)參數(shù)(pthread_attr_t *attr)表示線程的屬性。在上一個(gè)實(shí)例中,將該值設(shè)為NULL,也就是采用默認(rèn)屬性,線程的多項(xiàng)屬性都是可以更改的。這些屬性主要包括綁定屬性、分離屬性、堆棧地址、堆棧大小以及優(yōu)先級(jí)。其中系統(tǒng)默認(rèn)的屬性為非綁定、非分離、缺省1M的堆棧以及與父進(jìn)程同樣級(jí)別的優(yōu)先級(jí)。下面首先對(duì)綁定屬性和分離屬性的基本概念進(jìn)行講解。

n???? 綁定屬性。

前面已經(jīng)提到,Linux中采用“一對(duì)一”的線程機(jī)制,也就是一個(gè)用戶線程對(duì)應(yīng)一個(gè)內(nèi)核線程。綁定屬性就是指一個(gè)用戶線程固定地分配給一個(gè)內(nèi)核線程,因?yàn)?a class="article-link" target="_blank" href="/baike/1552575.html">CPU時(shí)間片的調(diào)度是面向內(nèi)核線程(也就是輕量級(jí)進(jìn)程)的,因此具有綁定屬性的線程可以保證在需要的時(shí)候總有一個(gè)內(nèi)核線程與之對(duì)應(yīng)。而與之對(duì)應(yīng)的非綁定屬性就是指用戶線程和內(nèi)核線程的關(guān)系不是始終固定的,而是由系統(tǒng)來控制分配的。

n???? 分離屬性。

分離屬性是用來決定一個(gè)線程以什么樣的方式來終止自己。在非分離情況下,當(dāng)一個(gè)線程結(jié)束時(shí),它所占用的系統(tǒng)資源并沒有被釋放,也就是沒有真正的終止。只有當(dāng)pthread_join()函數(shù)返回時(shí),創(chuàng)建的線程才能釋放自己占用的系統(tǒng)資源。而在分離屬性情況下,一個(gè)線程結(jié)束時(shí)立即釋放它所占有的系統(tǒng)資源。這里要注意的一點(diǎn)是,如果設(shè)置一個(gè)線程的分離屬性,而這個(gè)線程運(yùn)行又非??欤敲此芸赡茉趐thread_create()函數(shù)返回之前就終止了,它終止以后就可能將線程號(hào)和系統(tǒng)資源移交給其他的線程使用,這時(shí)調(diào)用pthread_create()的線程就得到了錯(cuò)誤的線程號(hào)。

這些屬性的設(shè)置都是通過特定的函數(shù)來完成的,通常首先調(diào)用pthread_attr_init()函數(shù)進(jìn)行初始化,之后再調(diào)用相應(yīng)的屬性設(shè)置函數(shù),最后調(diào)用pthread_attr_destroy()函數(shù)對(duì)分配的屬性結(jié)構(gòu)指針進(jìn)行清理和回收。設(shè)置綁定屬性的函數(shù)為pthread_attr_setscope(),設(shè)置線程分離屬性的函數(shù)為pthread_attr_setdetachstate(),設(shè)置線程優(yōu)先級(jí)的相關(guān)函數(shù)為pthread_attr_getschedparam()(獲取線程優(yōu)先級(jí))和pthread_attr_setschedparam()(設(shè)置線程優(yōu)先級(jí))。在設(shè)置完這些屬性后,就可以調(diào)用pthread_create()函數(shù)來創(chuàng)建線程了。

(2)函數(shù)格式。

表9.9列出了pthread_attr_init()函數(shù)的語法要點(diǎn)。

表9.9???????????????????????????????????????? pthread_attr_init()函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

int pthread_attr_init(pthread_attr_t *attr)

函數(shù)傳入值

attr:線程屬性結(jié)構(gòu)指針

函數(shù)返回值

成功:0

出錯(cuò):返回錯(cuò)誤碼

表9.10列出了pthread_attr_setscope()函數(shù)的語法要點(diǎn)。

表9.10????????????????????????????????? pthread_attr_setscope()函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

int pthread_attr_setscope(pthread_attr_t *attr, int scope)

函數(shù)傳入值

attr:線程屬性結(jié)構(gòu)指針

scope

PTHREAD_SCOPE_SYSTEM:綁定

PTHREAD_SCOPE_PROCESS:非綁定

函數(shù)返回值

成功:0

出錯(cuò):-1

表9.11列出了pthread_attr_setdetachstate()函數(shù)的語法要點(diǎn)。

表9.11???????????????????????????? pthread_attr_setdetachstate()函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

int pthread_attr_setscope(pthread_attr_t *attr, int detachstate)

函數(shù)傳入值

attr:線程屬性

detachstate

PTHREAD_CREATE_DETACHED:分離

PTHREAD _CREATE_JOINABLE:非分離

函數(shù)返回值

成功:0

出錯(cuò):返回錯(cuò)誤碼

表9.12列出了pthread_attr_getschedparam()函數(shù)的語法要點(diǎn)。

表9.12??????????????????????????? pthread_attr_getschedparam()函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

int pthread_attr_getschedparam (pthread_attr_t *attr, struct sched_param *param)

函數(shù)傳入值

attr:線程屬性結(jié)構(gòu)指針

param:線程優(yōu)先級(jí)

函數(shù)返回值

成功:0

出錯(cuò):返回錯(cuò)誤碼

表9.13列出了pthread_attr_setschedparam()函數(shù)的語法要點(diǎn)。

表9.13??????????????????????????? pthread_attr_setschedparam()函數(shù)語法要點(diǎn)

所需頭文件

#include <pthread.h>

函數(shù)原型

int pthread_attr_setschedparam (pthread_attr_t *attr, struct sched_param *param)

函數(shù)傳入值

attr:線程屬性結(jié)構(gòu)指針

param:線程優(yōu)先級(jí)

函數(shù)返回值

成功:0

出錯(cuò):返回錯(cuò)誤碼

?

(3)使用實(shí)例。

下面的實(shí)例是在我們已經(jīng)很熟悉的實(shí)例的基礎(chǔ)上增加線程屬性設(shè)置的功能。為了避免不必要的復(fù)雜性,這里就創(chuàng)建一個(gè)線程,這個(gè)線程具有綁定和分離屬性,而且主線程通過一個(gè)finish_flag標(biāo)志變量來獲得線程結(jié)束的消息,而并不調(diào)用pthread_join()函數(shù)。

/*thread_attr.c*/

#include <stdio.h>

#include <stdlib.h>

#include <pthread.h>

#define REPEAT_NUMBER???????? 3?????????? /* 線程中的小任務(wù)數(shù) */

#define DELAY_TIME_LEVELS??? 10.0?????? /* 小任務(wù)之間的最大時(shí)間間隔 */

int finish_flag = 0;

void *thrd_func(void *arg)

{

???? int delay_time = 0;

???? int count = 0;

????

???? printf("Thread is startingn");

???? for (count = 0; count < REPEAT_NUMBER; count++)

???? {

????????? delay_time = (int)(rand() * DELAY_TIME_LEVELS/(RAND_MAX)) + 1;

????????? sleep(delay_time);

????????? printf("tThread : job %d delay = %dn", count, delay_time);

???? }

???? printf("Thread finishedn");

???? finish_flag = 1;

???? pthread_exit(NULL);

}

int main(void)

{

???? pthread_t thread;

???? pthread_attr_t attr;

???? int no = 0, res;

???? void * thrd_ret;

????

???? srand(time(NULL));

???? /* 初始化線程屬性對(duì)象 */

???? res = pthread_attr_init(&attr);

???? if (res != 0)

???? {

????????? printf("Create attribute failedn");

????????? exit(res);

???? }

???? /* 設(shè)置線程綁定屬性 */

???? res = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

???? /* 設(shè)置線程分離屬性 */

???? res += pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

???? if (res != 0)

???? {

????????? printf("Setting attribute failedn");

????????? exit(res);

???? }

????

???? res = pthread_create(&thread, &attr, thrd_func, NULL);

???? if (res != 0)

???? {

????????? printf("Create thread failedn");

????????? exit(res);

???? }

???? /* 釋放線程屬性對(duì)象 */

???? pthread_attr_destroy(&attr);

???? printf("Create tread successn");

????

???? while(!finish_flag)

???? {

????????? printf("Waiting for thread to finish...n");

????????? sleep(2);

???? }

???? return 0;???????

}

?

接下來可以在線程運(yùn)行前后使用“free”命令查看內(nèi)存的使用情況。以下是運(yùn)行結(jié)果:

$ ./thread_attr

Create tread success

Waiting for thread to finish...

Thread is starting

Waiting for thread to finish...

??????? Thread : job 0 delay = 3

Waiting for thread to finish...

??????? Thread : job 1 delay = 2

Waiting for thread to finish...

Waiting for thread to finish...

Waiting for thread to finish...

Waiting for thread to finish...

??????? Thread : job 2 delay = 9

Thread finished

/* 程序運(yùn)行之前 */

$ free

???????????? total?????? used?????? free???? shared??? buffers???? cached

Mem:? ??????255556???? 191940????? 63616???????? 10?????? 5864????? 61360

-/+ buffers/cache:???? 124716???? 130840

Swap:?????? 377488????? 18352???? 359136

/* 程序運(yùn)行之中 */

$ free

???????????? total?????? used?????? free???? shared??? buffers???? cached

Mem:????? ??255556???? 191948????? 63608???????? 10?????? 5888????? 61336

-/+ buffers/cache:???? 124724???? 130832

Swap:?????? 377488????? 18352???? 359136

/* 程序運(yùn)行之后 */

$ free

???????????? total?????? used?????? free???? shared??? buffers???? cached

Mem:??????? 255556???? 191940????? 63616???????? 10?????? 5904????? 61320

-/+ buffers/cache:???? 124716???? 130840

Swap:?????? 377488????? 18352???? 359136

可以看到,線程在運(yùn)行結(jié)束后就收回了系統(tǒng)資源,并釋放內(nèi)存。

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

華清遠(yuǎn)見(www.farsight.com.cn)是國內(nèi)領(lǐng)先嵌入師培訓(xùn)機(jī)構(gòu),2004年注冊(cè)于中國北京海淀高科技園區(qū),除北京總部外,上海、深圳、成都、南京、武漢、西安、廣州均有直營分公司。華清遠(yuǎn)見除提供嵌入式相關(guān)的長(zhǎng)期就業(yè)培訓(xùn)、短期高端培訓(xùn)、師資培訓(xùn)及企業(yè)員工內(nèi)訓(xùn)等業(yè)務(wù)外,其下屬研發(fā)中心還負(fù)責(zé)嵌入式、Android及物聯(lián)網(wǎng)方向的教學(xué)實(shí)驗(yàn)平臺(tái)的研發(fā)及培訓(xùn)教材的出版,截止目前為止已公開出版70余本嵌入式/移動(dòng)開發(fā)/物聯(lián)網(wǎng)相關(guān)圖書。企業(yè)理念:專業(yè)始于專注 卓識(shí)源于遠(yuǎn)見。企業(yè)價(jià)值觀:做良心教育、做專業(yè)教育,更要做受人尊敬的職業(yè)教育。