8.线程
一.線程和進程的關系?
線程被稱為輕量級的進程(LWP),也有PCB, 創建線程和創建進程使用的底層函數都是一樣的。從內核里看進程和線程是一樣的,都有各自不同的PCB,但是PCB中指向內存資源的三級頁(4096)表是相同的。在linux下,線程是最小的執行單位,進程是最小的資源分配單位。CPU在執行的時候是以LWP為單位執行的。
當創建了一個進程后,系統為該進程分配0-4G的地址空間,當創建一個線程的時候不會再額外開辟地址空間,而是和之前的進程共享地址空間
查看當前運行的線程:
ps -eLf?
?查看某一進程的線程信息:
?ps -Lw pid
或?ps -Lf pid
內核是以進程為單位分配資源,操作系統在調度的時候則以LWP,也就是線程為單位調度的。
二.線程間的共享資源
線程與線程之間共享的部分包括
1.文件描述符表 2.每種信號的處理方式(sigaction 信號處理函數, 不共享信號屏蔽字, 每個線程內都有獨立的信號屏蔽字, 未決信號集也共享) 3.當前工作目錄(可通過chdir來改) 4.用戶ID和組ID 5.內存地址空間(0-3G 地址空間, 一個LWP通過malloc申請一塊地址,另一個同進程的LWP通過返回的地址也可以訪問到這塊內存)代碼段 數據段 已初始化的全局變量以及已初始化的靜態變量 BSS段 未初始化的全局變量以及未初始化的靜態變量, 其地址為0 堆 共享庫 (同一個共享庫在物理內存中只有一份,其余的進程可通過mmap映射到內存中)非共享的部分包括:
每個線程自己有自己獨立的棧, 注意不是內核棧(保存處理器現場),而是用戶空間的棧(變量存儲,開辟存儲空間) 線程ID( 不同于LWP,只在進程內有效,出了進程就無效了,主要是為了當前進程識別自己身份用的) errno 變量 信號屏蔽字 調度優先級線程的優缺點:
優點:提高程序并發性開銷小,不用重新分配內存通信和共享數據方便缺點:線程不穩定(庫函數實現) 線程調試比較困難(gdb支持不好)線程無法使用unix經典事件,例如信號可以在manpage中通過“man -k pthread” 查看線程相關函數,若無法查看,則需要安裝pthread的相關manpage
sudo apt-get install manpages-posix manpages-posix-dev三.線程相關函數?
◆pthread_create()
#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);arg1:?保存線程ID,注意傳遞的是地址。在Ubuntu 64位系統下類型為:typedef unsigned long int pthread_t
arg2: 線程屬性。創建一個線程,你希望它的用戶空間的棧有多大,采用默認屬性可以傳NULL,還可以用來設置線程優先級
arg3: 函數指針, 創建一個新的線程, 讓這個線程是是執行哪個函數,返回值void * ,參數也是void*
arg4: 就是函數里面的arg,就是啟用這個函數的時候,你想給里面傳入什么arg,也就是參數
需要注意 :線程編程的時候需要包含<pthread.h>, 線程是以獨立的庫形式提供的。所以需要加上 -lpthread, 引用的是libpthread.so這個庫文件。
◆pthread_self()
pthread_self(void); 獲取線程tid號◆pthread_exit()
#include <pthread.h> void pthread_exit(void *retval); void *retval:線程退出時傳遞出的參數,可以是退出值或地址,如是地址時,不能是線程內部申請的局部地址。這里需要引入另一個函數_exit(),?_exit實際上是exit函數的一個更底層函數,是由linux提供的底層函數,該函數直接導致進程退出,關閉未關閉的文件描述符。exit()關閉C標準文件流, 刷新緩沖區。
與exit()函數不同的是,pthread_exit()函數只會釋放當前線程,另外pthread_exit()或者return 返回的指針所指向的內存單元必須是全局的或者是用malloc分配的, 不能在線程函數的棧上分配,因為當其它函數得到這個返回指針時線程函數已經退出了。如果什么都不需要返回,可以返回NULL。
◆pthread_join()
#include <pthread.h> int pthread_join(pthread_t thread, void **retval); arg1: 回收線程的tid arg2: 接收退出線程傳遞出的返回值 返回值:成功返回0,失敗返回錯誤號該函數的作用主要在于:
1.阻塞。等待回收一個線程的資源,回收一個退出值,并且join函數在回收完這個線程后,會把這個線程的資源給回收掉。join函數可以回收線程的PCB,若不回收,則該線程會變成僵尸線程。成功返回0,失敗返回errno的值,可使用strerror打印。
2.接收調用函數的退出值。通過return 返回,retval就是return 返回值;通過pthread_exit()退出,retval就是傳給pthread_exit()的值,被別的線程通過pthread_cancel()終止,retval就是常數,PTHREAD_CANCELED ,其常數值為-1。如果對調用函數的返回值不感興趣,可以傳NULL。
該函數與pthread_create()函數組合用,有點類型于Android里面的AsyncTask框架。
◆pthread_cancel()
#include <pthread.h> int pthread_cancel(pthread_t thread);一個進程內的線程可以互相終止。類似于向某一個線程發信號。參數為:你要終止的線程ID
被取消的線程,退出值, 定義在linux的pthread庫中常數pthread_canceld的值是-1,
#define PTHREAD_CANCELED ((void *) -1)
例:pthread_create()與pthread_join()結合使用
#include <stdio.h> #include <stdlib.h> #include <pthread.h>/* AsyncTask::doInBackground() */ void *run(void *arg) {while(1) {printf("I'm sub thread, I'm running...\n");sleep(2);} }/* void *cancel_thread(void *arg) {printf("I'm cancel thread....\n");unsigned long *tid = (unsigned long *)arg;pthread_cancel(*tid); } */int main() {void *ret;pthread_t tid, cid;// AsyncTask::onPrepareExecuteprintf("I'm main thread, prepare.....\n");pthread_create(&tid, NULL, run, NULL); sleep(5);//pthread_create(&cid, NULL, cancel_thread, &tid); pthread_cancel(tid);// AsyncTask::onPostExecutepthread_join(tid, &ret);printf("I'm main thread, will end....\n"); printf("current return value is: %d\n", (int)ret);return 0; }運行結果:
I'm main thread, prepare.....
I'm sub thread, I'm running...
I'm sub thread, I'm running...
I'm sub thread, I'm running...
I'm main thread, will end....
current return value is: -1
◆pthread_detach()
#include <pthread.h> int pthread_detach(pthread_t tid);引入背景:某些線程不關閉它的退出值,但是還需要調用pthread_join()去回收它,顯得太過麻煩。因此就引入了分離態的概念。
置成分離態的線程,當它調用結束,系統會自動回收它的資源,不用主控線程再去回收。pthread_detach 與 pthread_join是互斥的。不能同時調用。也不能對一個處于detach狀態的函數調用pthread_join, 這樣的調用將返回EINVAL.
例:
運行結果:
I'm sub thread....I'm running....
I'm sub thread....I'm running....
I'm sub thread....I'm running....
I'm sub thread....I'm running....
I'm sub thread....I'm running....
I'm sub thread....I'm running....
I'm sub thread....I'm running....
I'm sub thread....I'm running....
◆pthread_equal()
#include <pthread.h> int pthread_equal(pthread_t t1, pthread_t t2);比較兩個函數是否相等,注意:如果兩個線程ID相等,則返回一個非0的值。否則,返回0。
?
總結
- 上一篇: Codepen 每日精选(2018-3-
- 下一篇: 《问道》手游开怼夜叉王 找对技巧咱就上