Android C++系列:Linux線程(三)線程屬性

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"linux下線程的屬性是可以根據實際項目需要,進行設置,之前我們討論的線程都是採用線程的默認屬性,默認屬性已經可以解決絕大多數開發時遇到的問 題。如我們對程序的性能提出更高的要求那麼需要設置線程屬性,比如可以通過設置線程棧的大小來降低內存的使用,增加最大線程個數。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"typedef struct {\n int etachstate; //線程的分離狀態\n int schedpolicy; //線程調度策略\n structsched_param schedparam; //線程的調度參數\n int inheritsched; //線程的繼承性\n int scope;//線程的作用域\n size_t guardsize;//線程棧末尾的警戒緩衝區大小\n int stackaddr_set; //線程的棧設置\n void* stackaddr; //線程棧的位置\n size_t stacksize; //線程棧的大小 \n}pthread_attr_t; \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注:目前線程屬性在內核中不是直接這麼定義的,抽象太深不便分析,爲方便理解,我們使用早期的線程屬性定義,兩者之間定義的主要元素差別不大。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"屬性值不能直接設置,須使用相關函數進行操作,初始化的函數爲pthread_attr_init, 這個函數必須在pthread_create函數之前調用。之後須用pthread_attr_destroy函數來釋 放資源。線程屬性主要包括如下屬性:作用域(scope)、棧尺寸(stack size)、棧地址 (stack address)、優先級(priority)、分離的狀態(detached state)、調度策略和 參數(scheduling policy and parameters)。默認的屬性爲非綁定、非分離、缺省M的堆 棧、與父進程同樣級別的優先級。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1. 線程屬性初始化","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先初始化線程屬性,再pthread_create創建線程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"#include \nint pthread_attr_init(pthread_attr_t *attr); //初始化線程屬性\nint pthread_attr_destroy(pthread_attr_t *attr); //銷燬線程屬性所佔用的資源\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"2. 線程的分離狀態(detached state)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程的分離狀態決定一個線程以什麼樣的方式來終止自己。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"非分離狀態:線程的默認屬性是非分離狀態,這種情況下,原有的線程等待創建的線程 結束。只有當pthread_join()函數返回時,創建的線程纔算終止,才能釋放自己佔用的系統 資源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分離狀態:分離線程沒有被其他的線程所等待,自己運行結束了,線程也就終止了,馬上釋放系統資源。應該根據自己的需要,選擇適當的分離狀態。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程分離狀態的函數:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"#include \nint pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); //設置線程屬性,分離or非分離\nint pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate); //獲取程屬性,分離or非分離\npthread_attr_t *attr:被已初始化的線程屬性\nint *detachstate:可選爲PTHREAD_CREATE_DETACHED(分離線程)和 PTHREAD _CREATE_JOINABLE(非分離線程)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏要注意的一點是,如果設置一個線程爲分離線程,而這個線程運行又非常快, 它很可能在pthread_create函數返回之前就終止了,它終止以後就可能將線程號和系統資源移交給其他的線程使用,這樣調用pthread_create的線程就得到了錯誤的線程號。 要避免這種情況可以採取一定的同步措施,最簡單的方法之一是可以在被創建的線程裏調用pthread_cond_timedwait函數,讓這個線程等待一會兒,留出足夠的時間讓函數 pthread_create返回。設置一段等待時間,是在多線程編程裏常用的方法。但是注意不要使 用諸如wait()之類的函數,它們是使整個進程睡眠,並不能解決線程同步的問題。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"3. 線程的棧地址(stack address)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"POSIX.1定義了兩個常量","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"_POSIX_THREAD_ATTR_STACKADDR","attrs":{}}],"attrs":{}},{"type":"text","text":" 和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"_POSIX_THREAD_ATTR_STACKSIZE","attrs":{}}],"attrs":{}},{"type":"text","text":" 檢測系統是否支持棧屬性。也可以給sysconf函數傳遞_SC_THREAD_ATTR_STACKADDR或 _SC_THREAD_ATTR_STACKSIZE 來進行檢測。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當進程棧地址空間不夠用時,指定新建線程使用由malloc分配的空間作爲自己的棧空 間。通過pthread_attr_setstackaddr和pthread_attr_getstackaddr兩個函數分別設置和獲 取線程的棧地址。傳給pthread_attr_setstackaddr函數的地址是緩衝區的低地址(不一定 是棧的開始地址,棧可能從高地址往低地址增長)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"include \nint pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);\nint pthread_attr_getstackaddr(pthread_attr_t *attr, void **stackaddr);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"attr: 指向一個線程屬性的指針","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"stackaddr: 返回獲取的棧地址 返回值:若是成功返回0,否則返回錯誤的編號","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"說 明:函數已過時,一般用下面講到的pthread_attr_getstack來代替","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"4. 線程的棧大小(stack size)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當系統中有很多線程時,可能需要減小每個線程棧的默認大小,防止進程的地址空間不 夠用,當線程調用的函數會分配很大的局部變量或者函數調用層次很深時,可能需要增大線 程棧的默認大小。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"函數pthread_attr_getstacksize和 pthread_attr_setstacksize提供設置。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"#include \nint pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); \nint pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"attr 指向一個線程屬性的指針","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"stacksize 返回線程的堆棧大小","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"返回值:若是成功返回0,否則返回錯誤的編號","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除上述對棧設置的函數外,還有以下兩個函數可以獲取和設置線程棧屬性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"#include \nint pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize); \nint pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"attr 指向一個線程屬性的指針","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"stackaddr 返回獲取的棧地址","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"stacksize 返回獲取的棧大小","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"返回值:若是成功返回0,否則返回錯誤的編號","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"5. 線程屬性控制實例","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"#include \n#include \n#include \n#include \n#define SIZE 0x10000\nint print_ntimes(char *str) {\n sleep(1); \n printf(\"%s\\n\", str); \n return 0;\n}\nvoid *th_fun(void *arg) {\n int n = 3; \n while (n--)\n print_ntimes(\"hello xwp\\n\"); \n}\n \nint main(void){\n pthread_t tid;\n int err, detachstate, i = 1; \n pthread_attr_t attr;\n size_t stacksize;\n void *stackaddr;\n pthread_attr_init(&attr);\n pthread_attr_getstack(&attr, &stackaddr, &stacksize); \n printf(\"stackadd=%p\\n\", stackaddr); \n printf(\"stacksize=%x\\n\", (int)stacksize);\n pthread_attr_getdetachstate(&attr, &detachstate); \n if (detachstate == PTHREAD_CREATE_DETACHED)\n printf(\"thread detached\\n\");\n else if (detachstate == PTHREAD_CREATE_JOINABLE)\n printf(\"thread join\\n\"); \n else\n printf(\"thread un known\\n\");\n /* 設置線程分離屬性 */\n pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); \n while (1) {\n /* 在堆上申請內存,指定線程棧的起始地址和大小 */ \n stackaddr = malloc(SIZE);\n if (stackaddr == NULL) {\n perror(\"malloc\");\n exit(1); \n }\n stacksize = SIZE;\n pthread_attr_setstack(&attr, stackaddr, stacksize);\n err = pthread_create(&tid, &attr, th_fun, NULL); \n if (err != 0) {\n printf(\"%s\\n\", strerror(err));\n exit(1); \n }\n printf(\"%d\\n\", i++); \n }\n pthread_attr_destroy(&attr);\n return 0;\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"6. 線程其他注意事項","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"6.1 NPTL","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"察看當前pthread庫版本","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"getconf GNU_LIBPTHREAD_VERSION","attrs":{}}],"attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"NPTL實現機制(POSIX),Native POSIX Thread Library","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"使用線程庫時gcc指定 -lpthread","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"6.2 線程終止方式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果需要只終止某個線程而不終止整個進程,可以有三種方法:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"從線程主函數return。這種方法對主控線程不適用,從main函數return相當於調用 exit;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"一個線程可以調用pthread_cancel終止同一進程中的另一個線程;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"線程可以調用pthread_exit終止自己。","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"6.3 細節注意","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"主線程退出其他線程不退出,主線程應調用ptrhed_exit ;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"避免僵線程:","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"join","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"pthread_deatch","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"pthread_create指定分離屬性","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"被join線程可能在join函數返回前就釋放完自己的所有內存資源,所以不應當返回被回收線程棧中的值;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"malloc和mmap申請的內存可以被其他線程釋放;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"如果線程終止時沒有釋放加鎖的互斥量,則該互斥量不能再被使用;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"應避免在多線程模型中調用fork除非,馬上exec,子進程中只有調用fork的線程存在,其他線程在子進程中均pthread_exit;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"信號的複雜語義很難和多線程共存,應避免在多線程引入信號機制","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"7. 總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文介紹了線程屬性:線程屬性初始化、線程的分離狀態、線程的棧地址、線程的棧大小;還介紹了線程的NPTL、線程終止方式、注意細節等注意事項。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章