一、首先,簡單瞭解一下多線程,從耳熟能詳的fork()、pthread中理點頭緒出來,然後自己寫一個簡單的來增加一下信心。
1、Linux系統下的多線程遵循POSIX線程接口,稱爲pthread。編寫Linux下的多線程程序,需要使用頭文件pthread.h,連 接時需要使用庫libpthread.a。因此,後面的編譯必須在選項中加入 -lpthread 選項,否則提示找不到pthread_create()這些函數。
2、pthread_t 是一個線程的標識符,創建線程用pthread_create(),等待線程結束用pthread_join(),這樣,差不多就可以開始寫第一個簡單的多線程程序了,簡單得類似HelloWorld
下面的是我自己寫的一個最簡單的程序。
/////// 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運行
結果:
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 .
不同的機器運行結果有可能不同,是由於兩個“並行”的線程搶奪處理器資源造成的。而sleep(i)是我自作聰明的想法,僅僅是爲了讓結果看上去更加能體現兩個線程是“並行”執行的,其實完全可以省去。
線程屬性:是否綁定、是否分離、堆棧地址、堆棧大小、優先級
默認的屬性:非綁定、非分離、缺省1M的堆棧、與父進程同樣級別的優先級。
########################
#######綁定輕進程#######
########################
關於線程的綁定,牽涉到另外一個概念:輕進程(LWP:Light Weight Process)。 輕進程可以理解爲內核線程,它位於用戶層和系統層之間。系統對線程資源的分配、對線程的控制是通過輕進程來實現的,一個輕進程可以控制一個或多個線程。默 認狀況下,啓動多少輕進程、哪些輕進程來控制哪些線程是由系統來控制的,這種狀況即稱爲非綁定的。綁定狀況下,則顧名思義,即某個線程固定的"綁"在一個輕進程之上。被綁定的線程具有較高的響應速度,這是因爲CPU時間片的調度是面向輕進程的,綁定的線程可以保證在需要的時候它總有一個輕進程可用。通過設置被綁定的輕進程的優先級和調度級可以使得綁定的線程滿足諸如實時反應之類的要求。
#i nclude <pthread.h>
pthread_attr_t attr;
pthread_t tid;
/*初始化屬性值,均設爲默認值*/
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);
########################
######設置分離狀態######
########################
線程的分離狀態決定一個線程以什麼樣的方式來終止自己。線程的默認屬性下,即爲非分離狀態,這種情況下,原有的線程等待創建的線程結束。只有當pthread_join()函數返回時,創建的線程纔算終止,才能釋放自己佔用的系統資源。而分離線程不是這樣子的,它沒有被其他的線程所等待,自己運行結束了,線程也就終止了,馬上釋放系統資源。程序員應該根據自己的需要,選擇適當的分離狀態。設置線程分離狀態的函數爲pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。第二個參數可選爲PTHREAD_CREATE_DETACHED(分離線程)和 PTHREAD _CREATE_JOINABLE(非分離線程)。這裏要注意的一點是,如果設置一個線程爲分離線程,而這個線程運行又非常快,它很可能在pthread_create函數返回之前就終止了,它終止以後就可能將線程號和系統資源移交給其他的線程使用,這樣調用pthread_create的線程就得到了錯誤的線程號。要避免這種情況可以採取一定的同步措施,最簡單的方法之一是可以在被創建的線程裏調用pthread_cond_timewait函數,讓這個線程等待一會兒,留出足夠的時間讓函數pthread_create返回。設置一段等待時間,是在多線程編程裏常用的方法。但是注意不要使用諸如wait()之類的函數,它們是使整個進程睡眠,並不能解決線程同步的問題。
########################
#######設置優先級#######
########################
另外一個可能常用的屬性是線程的優先級,它存放在結構sched_param中。用函數pthread_attr_getschedparam和函數pthread_attr_setschedparam進行存放,一般說來,我們總是先取優先級,對取得的值修改後再存放回去。下面即是一段簡單的例子。
#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是一個互斥鎖對象,互斥鎖是爲了防止多線程同時修改某一公共資源,我在下面的程序裏把它“鎖”在了一個叫buffer[10]的緩衝區 上,模型是2個Reader和2個Writer,Reader要等到叫buffer的書架上有書的時候纔可以read,而Writer也必須在書架沒有放 滿的情況下纔可以把新寫的書放到書架上。我的程序裏書架的大小是1,當然也可以設置書架的大小,不過實現過程大同小異。就不多說了。來看程序:
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 /* 用默認屬性初始化一個互斥鎖對象*/
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 /* 打開互斥鎖*/
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
運行:./mutex
結果:
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()不再是爲了讓結果好看, 而是爲了防止一個線程始終佔用資源,很多網上的教程使用的是pthread_delay_np(&delay);這個語句,不過這個似乎只能在 solaris系統上用,linux還是用sleep()和usleep()好了,有高人說可能會使該線程所在的進程都sleep,我使用下來似乎沒有發 現。而且似乎linux下本來就是一個進程裏只有一個線程,忘記哪個手冊上說的,也許是以前的版本,不追究了,能用就行。
還 有就是在reader()和writer()裏,我用了很奇怪的 i 和 ti 兩個變量來對reader和writer編號,主要目的是爲了保證每個Reader和Writer都能完成自己的使命,如果按照常規寫法,使用for(i =1;i<3;i++)這樣控制循環會使有的Reader或者Writer不能取到或者放上書。似乎說得自己都迷糊了,舉例說,Reader1去取 書,恰巧這時候書架上是空的,爲了保證Reader1能取到書,我就讓Reader們排隊,直到Reader1取到以後才輪到Reader2,對於 Writer們也實行排隊
來自:http://hi.baidu.com/%BC%D0%B2%E3%D6%D0%B5%C4%BB%B0%CC%E2/blog/item/fff6a21e8f36e91f40341706.html