摘自資料(linux 與Windows不同)
線程間無需特別的手段進(jìn)行通信,因?yàn)榫€程間可以共享數(shù)據(jù)結(jié)構(gòu),也就是一個(gè)全局變量可以被兩個(gè)線程同時(shí)使用。不過要注意的是線程間需要做好同步,一般用
mutex??梢詤⒖家恍┍容^新的UNIX/Linux編程的書,都會(huì)提到Posix線程編程,比如《UNIX環(huán)境高級(jí)編程(第二版)》、《UNIX系統(tǒng)
編程》等等。 linux的消息屬于IPC,也就是進(jìn)程間通信,線程用不上。
linux用pthread_kill對(duì)線程發(fā)信號(hào)。 另:windows下不是用post..(你是說PostMessage嗎?)進(jìn)行線程通信的吧?
windows用PostThreadMessage進(jìn)行線程間通信,但實(shí)際上極少用這種方法。還是利用同步多一些 LINUX下的同步和Windows原理都是一樣的。不過Linux下的singal中斷也很好用。
用好信號(hào)量,共享資源就可以了。
使用多線程的理由之一是和進(jìn)程相比,它是一種非常"節(jié)儉"的多任務(wù)操作方式。我們知道,在Linux系統(tǒng)下,啟動(dòng)一個(gè)新的進(jìn)程必須分配給它獨(dú)立的
地址空間,建立眾多的數(shù)據(jù)表來維護(hù)它的代碼段、堆棧段和數(shù)據(jù)段,這是一種"昂貴"的多任務(wù)工作方式。而運(yùn)行于一個(gè)進(jìn)程中的多個(gè)線程,它們彼此之間使用相同
的地址空間,共享大部分?jǐn)?shù)據(jù),啟動(dòng)一個(gè)線程所花費(fèi)的空間遠(yuǎn)遠(yuǎn)小于啟動(dòng)一個(gè)進(jìn)程所花費(fèi)的空間,而且,線程間彼此切換所需的時(shí)間也遠(yuǎn)遠(yuǎn)小于進(jìn)程間切換所需要的
時(shí)間。
使用多線程的理由之二是線程間方便的通信機(jī)
制。對(duì)不同進(jìn)程來說,它們具有獨(dú)立的數(shù)據(jù)空間,要進(jìn)行數(shù)據(jù)的傳遞只能通過通信的方式進(jìn)行,這種方式不僅費(fèi)時(shí),而且很不方便。線程則不然,由于同一進(jìn)程下的
線程之間共享數(shù)據(jù)空間,所以一個(gè)線程的數(shù)據(jù)可以直接為其它線程所用,這不僅快捷,而且方便。當(dāng)然,數(shù)據(jù)的共享也帶來其他一些問題,有的變量不能同時(shí)被兩個(gè)
線程所修改,有的子程序中聲明為static的數(shù)據(jù)更有可能給多線程程序帶來災(zāi)難性的打擊,這些正是編寫多線程程序時(shí)最需要注意的地方。
1、簡單的多線程程序
首先在主函數(shù)中,我們使用到了兩個(gè)函數(shù),pthread_create和pthread_join,并聲明了一個(gè)pthread_t型的變量。
pthread_t在頭文件pthread.h中已經(jīng)聲明,是線程的標(biāo)示符
函數(shù)pthread_create用來創(chuàng)建一個(gè)線程,函數(shù)原型:
extern int pthread_create __P ((pthread_t
*__thread, __const pthread_attr_t *__attr,void *(*__start_routine) (void
*), void *__arg));
第一個(gè)參數(shù)為指向線程標(biāo)識(shí)符的指針,第二個(gè)參數(shù)用來設(shè)置線程屬性,第三個(gè)參數(shù)
是線程運(yùn)行函數(shù)的起始地址,最后一個(gè)參數(shù)是運(yùn)行函數(shù)的參數(shù)。若我們的函數(shù)thread不需要參數(shù),所以最后一個(gè)參數(shù)設(shè)為空指針。第二個(gè)參數(shù)我們也設(shè)為空指
針,這樣將生成默認(rèn)屬性的線程。對(duì)線程屬性的設(shè)定和修改我們將在下一節(jié)闡述。當(dāng)創(chuàng)建線程成功時(shí),函數(shù)返回0,若不為0則說明創(chuàng)建線程失敗,常見的錯(cuò)誤返回
代碼為EAGAIN和EINVAL。前者表示系統(tǒng)限制創(chuàng)建新的線程,例如線程數(shù)目過多了;后者表示第二個(gè)參數(shù)代表的線程屬性值非法。創(chuàng)建線程成功后,新創(chuàng)
建的線程則運(yùn)行參數(shù)三和參數(shù)四確定的函數(shù),原來的線程則繼續(xù)運(yùn)行下一行代碼。
函數(shù)pthread_join用來等待一個(gè)線程的結(jié)束。函數(shù)原型為:
extern int pthread_join __P ((pthread_t __th, void **__thread_return));
第一個(gè)參數(shù)為被等待的線程標(biāo)識(shí)符,第二個(gè)參數(shù)為一個(gè)用戶定義的指針,它可以用來存儲(chǔ)被
等待線程的返回值。這個(gè)函數(shù)是一個(gè)線程阻塞的函數(shù),調(diào)用它的函數(shù)將一直等待到被等待的線程結(jié)束為止,當(dāng)函數(shù)返回時(shí),被等待線程的資源被收回。一個(gè)線程的結(jié)
束有兩種途徑,一種是象我們上面的例子一樣,函數(shù)結(jié)束了,調(diào)用它的線程也就結(jié)束了;另一種方式是通過函數(shù)pthread_exit來實(shí)現(xiàn)。它的函數(shù)原型
為:
extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));
唯一的參數(shù)是函數(shù)的返回代碼,只要pthread_join中的第二個(gè)參數(shù)thread_return不是NULL,這個(gè)值將被傳遞給
thread_return。最后要說明的是,一個(gè)線程不能被多個(gè)線程等待,否則第一個(gè)接收到信號(hào)的線程成功返回,其余調(diào)用pthread_join的線
程則返回錯(cuò)誤代碼ESRCH。
2、修改線程的屬性
設(shè)置線程綁定狀態(tài)的函數(shù)為pthread_attr_setscope,它有兩個(gè)參數(shù),第一個(gè)是指向?qū)傩越Y(jié)構(gòu)的指針,第
二個(gè)是綁定類型,它有兩個(gè)取值:PTHREAD_SCOPE_SYSTEM(綁定的)和PTHREAD_SCOPE_PROCESS(非綁定的)。下面的
代碼即創(chuàng)建了一個(gè)綁定的線程。
#include
pthread_attr_t attr;
pthread_t tid;
/*初始化屬性值,均設(shè)為默認(rèn)值*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_create(&tid, &attr, (void *) my_function, NULL);
3、線程的數(shù)據(jù)處理
和進(jìn)程相比,線程的最大優(yōu)點(diǎn)之一是數(shù)據(jù)的共享性,各個(gè)進(jìn)程共享父進(jìn)程處沿襲的數(shù)據(jù)段,可以方便的獲得、修改數(shù)據(jù)。但這也給多線程編程帶來了許多問
題。我們必須當(dāng)心有多個(gè)不同的進(jìn)程訪問相同的變量。許多函數(shù)是不可重入的,即同時(shí)不能運(yùn)行一個(gè)函數(shù)的多個(gè)拷貝(除非使用不同的數(shù)據(jù)段)。在函數(shù)中聲明的靜
態(tài)變量常常帶來問題,函數(shù)的返回值也會(huì)有問題。因?yàn)槿绻祷氐氖呛瘮?shù)內(nèi)部靜態(tài)聲明的空間的地址,則在一個(gè)線程調(diào)用該函數(shù)得到地址后使用該地址指向的數(shù)據(jù)
時(shí),別的線程可能調(diào)用此函數(shù)并修改了這一段數(shù)據(jù)。在進(jìn)程中共享的變量必須用關(guān)鍵字volatile來定義,這是為了防止編譯器在優(yōu)化時(shí)(如gcc中使用
-OX參數(shù))改變它們的使用方式。為了保護(hù)變量,我們必須使用信號(hào)量、互斥等方法來保證我們對(duì)變量的正確使用。
4、互斥鎖
互斥鎖用來保證一段時(shí)間內(nèi)只有一個(gè)線程在執(zhí)行一段代碼。必要性顯而易見:假設(shè)各個(gè)線程向同一個(gè)文件順序?qū)懭霐?shù)據(jù),最后得到的結(jié)果一定是災(zāi)難性的