一、首先,簡(jiǎn)單了解一下多線程,從耳熟能詳?shù)膄ork()、pthread中理點(diǎn)頭緒出來(lái),然后自己寫一個(gè)簡(jiǎn)單的來(lái)增加一下信心。
1、Linux系統(tǒng)下的多線程遵循POSIX線程接口,稱為pthread。編寫Linux下的多線程程序,需要使用頭文件pthread.h,連 接時(shí)需要使用庫(kù)libpthread.a。因此,后面的編譯必須在選項(xiàng)中加入 -lpthread 選項(xiàng),否則提示找不到pthread_create()這些函數(shù)。
2、pthread_t 是一個(gè)線程的標(biāo)識(shí)符,創(chuàng)建線程用pthread_create(),等待線程結(jié)束用pthread_join(),這樣,差不多就可以開(kāi)始寫第一個(gè)簡(jiǎn)單的多線程程序了,簡(jiǎn)單得類似HelloWorld
下面的是我自己寫的一個(gè)最簡(jiǎn)單的程序。
/////// simpleone.c
1 #i nclude <stdio.h>
2 #i nclude <pthread.h>
3
4 void thread()
5 {
6 int i;
7 for (i=0;i<3;i++)
8 {
9 printf("This is thread %d .\r\n",i);
10 sleep(i);
11 }
12 }
13
14 int main()
15 {
16 pthread_t id;
17 int i,ret;
18 ret = pthread_create(&id,NULL,(void *)thread,NULL);
19 if (ret != 0)
20 {
21 printf("Create thread error!\r\n");
22 exit(1);
23 }
24 for (i=0;i<3;i++)
25 {
26 printf("This is the main thread %d .\r\n",i);
27 sleep(i);
28 }
29 pthread_join(id,NULL);
30 return(0);
31 }
32
使用gcc -g -lpthread simpleone.c 編譯
使用./a.out運(yùn)行
結(jié)果:
This is thread 0 .
This is thread 1 .
This is the main thread 0 .
This is the main thread 1 .
This is thread 2 .
This is the main thread 2 .
不同的機(jī)器運(yùn)行結(jié)果有可能不同,是由于兩個(gè)“并行”的線程搶奪處理器資源造成的。而sleep(i)是我自作聰明的想法,僅僅是為了讓結(jié)果看上去更加能體現(xiàn)兩個(gè)線程是“并行”執(zhí)行的,其實(shí)完全可以省去。
線程屬性:是否綁定、是否分離、堆棧地址、堆棧大小、優(yōu)先級(jí)
默認(rèn)的屬性:非綁定、非分離、缺省1M的堆棧、與父進(jìn)程同樣級(jí)別的優(yōu)先級(jí)。
########################
#######綁定輕進(jìn)程#######
########################
關(guān)于線程的綁定,牽涉到另外一個(gè)概念:輕進(jìn)程(LWP:Light Weight Process)。 輕進(jìn)程可以理解為內(nèi)核線程,它位于用戶層和系統(tǒng)層之間。系統(tǒng)對(duì)線程資源的分配、對(duì)線程的控制是通過(guò)輕進(jìn)程來(lái)實(shí)現(xiàn)的,一個(gè)輕進(jìn)程可以控制一個(gè)或多個(gè)線程。默 認(rèn)狀況下,啟動(dòng)多少輕進(jìn)程、哪些輕進(jìn)程來(lái)控制哪些線程是由系統(tǒng)來(lái)控制的,這種狀況即稱為非綁定的。綁定狀況下,則顧名思義,即某個(gè)線程固定的"綁"在一個(gè)輕進(jìn)程之上。被綁定的線程具有較高的響應(yīng)速度,這是因?yàn)镃PU時(shí)間片的調(diào)度是面向輕進(jìn)程的,綁定的線程可以保證在需要的時(shí)候它總有一個(gè)輕進(jìn)程可用。通過(guò)設(shè)置被綁定的輕進(jìn)程的優(yōu)先級(jí)和調(diào)度級(jí)可以使得綁定的線程滿足諸如實(shí)時(shí)反應(yīng)之類的要求。
#i nclude <pthread.h>
pthread_attr_t attr;
pthread_t tid;
/*初始化屬性值,均設(shè)為默認(rèn)值*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
/*PTHREAD_SCOPE_SYSTEM(綁定的) PTHREAD_SCOPE_PROCESS(非綁定的)*/
pthread_create(&tid, &attr, (void *) my_function, NULL);
########################
######設(shè)置分離狀態(tài)######
########################
線程的分離狀態(tài)決定一個(gè)線程以什么樣的方式來(lái)終止自己。線程的默認(rèn)屬性下,即為非分離狀態(tài),這種情況下,原有的線程等待創(chuàng)建的線程結(jié)束。只有當(dāng)pthread_join()函數(shù)返回時(shí),創(chuàng)建的線程才算終止,才能釋放自己占用的系統(tǒng)資源。而分離線程不是這樣子的,它沒(méi)有被其他的線程所等待,自己運(yùn)行結(jié)束了,線程也就終止了,馬上釋放系統(tǒng)資源。程序員應(yīng)該根據(jù)自己的需要,選擇適當(dāng)?shù)姆蛛x狀態(tài)。設(shè)置線程分離狀態(tài)的函數(shù)為pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二個(gè)參數(shù)可選為PTHREAD_CREATE_DETACHED(分離線程)和 PTHREAD _CREATE_JOINABLE(非分離線程)。這里要注意的一點(diǎn)是,如果設(shè)置一個(gè)線程為分離線程,而這個(gè)線程運(yùn)行又非???,它很可能在pthread_create函數(shù)返回之前就終止了,它終止以后就可能將線程號(hào)和系統(tǒng)資源移交給其他的線程使用,這樣調(diào)用pthread_create的線程就得到了錯(cuò)誤的線程號(hào)。要避免這種情況可以采取一定的同步措施,最簡(jiǎn)單的方法之一是可以在被創(chuàng)建的線程里調(diào)用pthread_cond_timewait函數(shù),讓這個(gè)線程等待一會(huì)兒,留出足夠的時(shí)間讓函數(shù)pthread_create返回。設(shè)置一段等待時(shí)間,是在多線程編程里常用的方法。但是注意不要使用諸如wait()之類的函數(shù),它們是使整個(gè)進(jìn)程睡眠,并不能解決線程同步的問(wèn)題。
########################
#######設(shè)置優(yōu)先級(jí)#######
########################
另外一個(gè)可能常用的屬性是線程的優(yōu)先級(jí),它存放在結(jié)構(gòu)sched_param中。用函數(shù)pthread_attr_getschedparam和函數(shù)pthread_attr_setschedparam進(jìn)行存放,一般說(shuō)來(lái),我們總是先取優(yōu)先級(jí),對(duì)取得的值修改后再存放回去。下面即是一段簡(jiǎn)單的例子。
#i nclude <pthread.h>
#i nclude <sched.h>
pthread_attr_t attr;
pthread_t tid;
sched_param param;
int newprio=20;
pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr, ¶m);
param.sched_priority=newprio;
pthread_attr_setschedparam(&attr, ¶m);
pthread_create(&tid, &attr, (void *)myfunction, myarg);
About Mutex
mutex是一個(gè)互斥鎖對(duì)象,互斥鎖是為了防止多線程同時(shí)修改某一公共資源,我在下面的程序里把它“鎖”在了一個(gè)叫buffer[10]的緩沖區(qū) 上,模型是2個(gè)Reader和2個(gè)Writer,Reader要等到叫buffer的書(shū)架上有書(shū)的時(shí)候才可以read,而Writer也必須在書(shū)架沒(méi)有放 滿的情況下才可以把新寫的書(shū)放到書(shū)架上。我的程序里書(shū)架的大小是1,當(dāng)然也可以設(shè)置書(shū)架的大小,不過(guò)實(shí)現(xiàn)過(guò)程大同小異。就不多說(shuō)了。來(lái)看程序:
1 #i nclude <stdio.h>
2 #i nclude <pthread.h>
3
4 void reader_function(void);
5 void writer_function(void);
6
7 char buffer[10]={0};
8 int buffer_has_item=0;
9 pthread_mutex_t mutex;
10
11 int main(void)
12 {
13 pthread_t reader;
14
15 /* 用默認(rèn)屬性初始化一個(gè)互斥鎖對(duì)象*/
16 pthread_mutex_init (&mutex,NULL);
17 pthread_create(&reader,NULL, (void *)reader_function, NULL);
18 writer_function();
19 }
20
21 void writer_function(void)
22 {
23 int i,ti;
24
25 for(i=1;i<3;)
26 {
27 ti=i;
28 /* 鎖定互斥鎖*/
29 pthread_mutex_lock(&mutex);
30 printf("Writer %d Locked Buffer.\r\n",i);
31 if (buffer_has_item==0)
32 {
33 buffer_has_item=1;
34 strcpy(buffer,"Full");
35 printf("++Writer %d fill the buffer with context \"%s\".\r\n",i,buffer);
36 i++;
37 }
38 /* 打開(kāi)互斥鎖*/
39 printf("Writer %d Unlocked Buffer.\r\n\n\n",ti);
40 pthread_mutex_unlock(&mutex);
41 sleep(1);
42 }
43
44 }
45
46 void reader_function(void)
47 {
48 int i,ti;
49 for(i=1;i<3;)
50 {
51 ti=i;
52 pthread_mutex_lock(&mutex);
53 printf("Reader %d Locked Buffer.\r\n",i);
54 if(buffer_has_item==1)
55 {
56 buffer_has_item=0;
57 strcpy(buffer,"Empty");
58 printf("--Reader %d clean the buffer with context \"%s\".\r\n",i,buffer);
59 i++;
60 }
61 printf("Reader %d Unlocked Buffer.\r\n\n\n",ti);
62 pthread_mutex_unlock(&mutex);
63 sleep(1);
64 }
65
66 }
67
編譯:gcc -o mutex -lpthread -g mutex.c
運(yùn)行:./mutex
結(jié)果:
Reader 1 Locked Buffer.
Reader 1 Unlocked Buffer.
Writer 1 Locked Buffer.
++Writer 1 fill the buffer with context "Full".
Writer 1 Unlocked Buffer.
Writer 2 Locked Buffer.
Writer 2 Unlocked Buffer.
Reader 1 Locked Buffer.
--Reader 1 clean the buffer with context "Empty".
Reader 1 Unlocked Buffer.
Writer 2 Locked Buffer.
++Writer 2 fill the buffer with context "Full".
Writer 2 Unlocked Buffer.
Reader 2 Locked Buffer.
--Reader 2 clean the buffer with context "Empty".
Reader 2 Unlocked Buffer.
注意,這次用到的sleep()不再是為了讓結(jié)果好看, 而是為了防止一個(gè)線程始終占用資源,很多網(wǎng)上的教程使用的是pthread_delay_np(&delay);這個(gè)語(yǔ)句,不過(guò)這個(gè)似乎只能在 solaris系統(tǒng)上用,linux還是用sleep()和usleep()好了,有高人說(shuō)可能會(huì)使該線程所在的進(jìn)程都sleep,我使用下來(lái)似乎沒(méi)有發(fā) 現(xiàn)。而且似乎linux下本來(lái)就是一個(gè)進(jìn)程里只有一個(gè)線程,忘記哪個(gè)手冊(cè)上說(shuō)的,也許是以前的版本,不追究了,能用就行。
還 有就是在reader()和writer()里,我用了很奇怪的 i 和 ti 兩個(gè)變量來(lái)對(duì)reader和writer編號(hào),主要目的是為了保證每個(gè)Reader和Writer都能完成自己的使命,如果按照常規(guī)寫法,使用for(i =1;i<3;i++)這樣控制循環(huán)會(huì)使有的Reader或者Writer不能取到或者放上書(shū)。似乎說(shuō)得自己都迷糊了,舉例說(shuō),Reader1去取 書(shū),恰巧這時(shí)候書(shū)架上是空的,為了保證Reader1能取到書(shū),我就讓Reader們排隊(duì),直到Reader1取到以后才輪到Reader2,對(duì)于 Writer們也實(shí)行排隊(duì)
來(lái)自:http://hi.baidu.com/%BC%D0%B2%E3%D6%D0%B5%C4%BB%B0%CC%E2/blog/item/fff6a21e8f36e91f40341706.html
聯(lián)系客服