創建POSIX線程的函數為
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
第1個參數為線程句柄(類似於文件描述符),第3個參數為線程啟動函數(輸入void*、返回void*,因為指向任何結構體/基本數據類型的指針都可以被看作void*,而void*一般都可以顯式強制轉換成指向對應類型的指針甚至整型,這是不支持純C編程的常見技巧),第4個參數為傳遞給線程啟動函數的參數。
而第2個參數一般都設置為NULL,此時采用默認的線程屬性()。但是有需求的時候還是得去設置,即使是TLPI上也沒有深入線程屬性而只是在29.8線程屬性一節給出了個例子。於是我通過結合手冊以及查看pthread.h來深入了解其用法。
首先貼出TLPI上線程屬性的設置示例(的完整版),該線程使用了分離(detach)屬性,即線程創建后自動分離,而無需調用pthread_detach函數
// thread_attrib.c: 線程屬性使用示例
// 編譯方式: gcc thread_attrib.c -pthread
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
// 檢查錯誤碼errnum, 若不為0則輸出msg以及錯誤碼相關信息
static inline void checkErrnum(int errnum, const char* msg)
{
if (errnum != 0) {
fprintf(stderr, "%s error: %s\n", msg, strerror(errnum));
exit(1);
}
}
// 線程啟動函數, 將輸入看作字符串類型
static inline void* threadFunc(void* arg)
{
printf("%s\n", (char*) arg);
return NULL;
}
int main()
{
int s; // 錯誤碼
// 初始化線程屬性
pthread_attr_t attr;
s = pthread_attr_init(&attr);
checkErrnum(s, "pthread_attr_init");
// 設置線程分離狀態, 此屬性保證線程
s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
checkErrnum(s, "pthread_attr_setdetachstate");
// 使用attr屬性創建線程
pthread_t tid;
s = pthread_create(&tid, &attr, threadFunc, (void*) "Hello world!");
checkErrnum(s, "pthread_create");
// 銷毀線程屬性
s = pthread_attr_destroy(&attr);
checkErrnum(s, "pthread_attr_destroy");
sleep(1); // main線程休眠, 保證創建線程的threadFunc函數能夠執行完畢
return 0;
}
需要注意的一點是:使用pthread_attr_init初始化線程屬性,使用完(即傳入pthread_create)后需要使用pthread_attr_destory銷毀,從而釋放相關資源。
同時也可以發現,線程屬性的設置方式並不像那樣簡單,一般純C中設置屬性往往是通過亦或運算來設置不同類型。
比如系統調用open的第3個參數可以用S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH這樣4個定義為整型的宏進行亦或運算來表示rw-r--r--的讀/寫/執行權限。
而線程屬性的設置,對每個屬性都有單獨的函數,而每個函數都有各自的選項,比如這里的pthread_attr_setdetachstate,關鍵詞是detachstate(分離狀態)。第2個參數這里選擇了PTHREAD_CREATE_DETACHED,代表線程創建后立刻分離
於是查看線程屬性類型的定義
union pthread_attr_t { char __size[__SIZEOF_PTHREAD_ATTR_T]; long int __align; }; #ifndef __have_pthread_attr_t typedef union pthread_attr_t pthread_attr_t; # define __have_pthread_attr_t 1 #endif
之前open的第3個參數相當於是0b0001、0b0010、0b0100這種類型的值求亦或,可以看做是位(bit)數組,數組的每一位只能是0或1,而每一個線程屬性都可以有多個值,於是pthread_attr_t則是一個char數組,char的取值有256個,也就是說,每一個線程屬性理論上可以支持最多256個可選值。數組大小_SIZEOF_PTHREA_ATTR_T則是由系統類型以及WORDSIZE來決定。
這里通過查看glib庫的頭文件pthread.h來一探究竟,看看線程屬性有哪些。線程屬性的設置函數的前綴都是pthread_attr_set,所以查找以此為前綴的函數即可。我將找到的函數列表整理如下
1、線程分離狀態(Detach state),默認是JOINABLE,即其他線程可以通過pthread_join函數來取得該線程的返回結果(“連接”);DETACHED則是線程創建時即分離,無法被其他線程連接。
/* Detach state. */ enum { PTHREAD_CREATE_JOINABLE, #define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_DETACHED #define PTHREAD_CREATE_DETACHED PTHREAD_CREATE_DETACHED };
/* Get detach state attribute. */ extern int pthread_attr_getdetachstate (const pthread_attr_t *__attr, int *__detachstate) __THROW __nonnull ((1, 2)); /* Set detach state attribute. */ extern int pthread_attr_setdetachstate (pthread_attr_t *__attr, int __detachstate) __THROW __nonnull ((1));
2、線程棧末尾之后用以避免棧溢出的擴展內存的大小,若設置為0,則代表這種機制無效
/* Get the size of the guard area created for stack overflow protection. */ extern int pthread_attr_getguardsize (const pthread_attr_t *__attr, size_t *__guardsize) __THROW __nonnull ((1, 2)); /* Set the size of the guard area created for stack overflow protection. */ extern int pthread_attr_setguardsize (pthread_attr_t *__attr, size_t __guardsize) __THROW __nonnull ((1));
3、調度參數,類型為結構體struct sched_param,第1個參數為優先級,其他的參數未知。可移植的做法就是設置線程優先級。
/* The official definition. */ struct sched_param { int __sched_priority; };
/* Return in *PARAM the scheduling parameters of *ATTR. */ extern int pthread_attr_getschedparam (const pthread_attr_t *__restrict __attr, struct sched_param *__restrict __param) __THROW __nonnull ((1, 2)); /* Set scheduling parameters (priority, etc) in *ATTR according to PARAM. */ extern int pthread_attr_setschedparam (pthread_attr_t *__restrict __attr, const struct sched_param *__restrict __param) __THROW __nonnull ((1, 2));
4、調度策略,SCHED_OTHER為默認值(分時調度策略),SCHED_FIFO和SCHED_RR均為實時調度策略,前者是先到先服務,后者是時間片輪轉。
具體細節可以參考文章
線程調度策略SCHED_RR(輪轉法)和SCHED_FIFO(先進先出)之對比
/* Return in *POLICY the scheduling policy of *ATTR. */ extern int pthread_attr_getschedpolicy (const pthread_attr_t *__restrict __attr, int *__restrict __policy) __THROW __nonnull ((1, 2)); /* Set scheduling policy in *ATTR according to POLICY. */ extern int pthread_attr_setschedpolicy (pthread_attr_t *__attr, int __policy) __THROW __nonnull ((1));
// bits/sched.h /* Scheduling algorithms. */ #define SCHED_OTHER 0 #define SCHED_FIFO 1 #define SCHED_RR 2
5、調度器的繼承模式,默認是INHERIT,繼承自父線程的調度優先級,只有設置為EXPLICIT,自己“顯式”設置的調度策略、優先級才會起作用。
/* Scheduler inheritance. */ enum { PTHREAD_INHERIT_SCHED, #define PTHREAD_INHERIT_SCHED PTHREAD_INHERIT_SCHED PTHREAD_EXPLICIT_SCHED #define PTHREAD_EXPLICIT_SCHED PTHREAD_EXPLICIT_SCHED };
/* Return in *INHERIT the scheduling inheritance mode of *ATTR. */ extern int pthread_attr_getinheritsched (const pthread_attr_t *__restrict __attr, int *__restrict __inherit) __THROW __nonnull ((1, 2)); /* Set scheduling inheritance mode in *ATTR according to INHERIT. */ extern int pthread_attr_setinheritsched (pthread_attr_t *__attr, int __inherit) __THROW __nonnull ((1));
6、調度內容的作用域,SYSTEM代表該線程與系統的所有線程競爭資源,PROCESS代表該線程與進程中的其他線程競爭資源。
/* Scope handling. */ enum { PTHREAD_SCOPE_SYSTEM, #define PTHREAD_SCOPE_SYSTEM PTHREAD_SCOPE_SYSTEM PTHREAD_SCOPE_PROCESS #define PTHREAD_SCOPE_PROCESS PTHREAD_SCOPE_PROCESS };
/* Return in *SCOPE the scheduling contention scope of *ATTR. */ extern int pthread_attr_getscope (const pthread_attr_t *__restrict __attr, int *__restrict __scope) __THROW __nonnull ((1, 2)); /* Set scheduling contention scope in *ATTR according to SCOPE. */ extern int pthread_attr_setscope (pthread_attr_t *__attr, int __scope) __THROW __nonnull ((1));
7、線程的棧地址
/* Return the previously set address for the stack. */ extern int pthread_attr_getstackaddr (const pthread_attr_t *__restrict __attr, void **__restrict __stackaddr) __THROW __nonnull ((1, 2)) __attribute_deprecated__; /* Set the starting address of the stack of the thread to be created. Depending on whether the stack grows up or down the value must either be higher or lower than all the address in the memory block. The minimal size of the block must be PTHREAD_STACK_MIN. */ extern int pthread_attr_setstackaddr (pthread_attr_t *__attr, void *__stackaddr) __THROW __nonnull ((1)) __attribute_deprecated__;
8、當前使用的最小棧地址的大小,當設置這個值時,棧的大小不能小於PTHREAD_STACK_MIN,不能大於系統的限制(用命令ulimit -s查看)
/* Return the currently used minimal stack size. */ extern int pthread_attr_getstacksize (const pthread_attr_t *__restrict __attr, size_t *__restrict __stacksize) __THROW __nonnull ((1, 2)); /* Add information about the minimum stack size needed for the thread to be started. This size must never be less than PTHREAD_STACK_MIN and must also not exceed the system limits. */ extern int pthread_attr_setstacksize (pthread_attr_t *__attr, size_t __stacksize) __THROW __nonnull ((1));
9、線程棧的地址和大小(需要定義宏__USE_XOPEN2K)
#ifdef __USE_XOPEN2K /* Return the previously set address for the stack. */ extern int pthread_attr_getstack (const pthread_attr_t *__restrict __attr, void **__restrict __stackaddr, size_t *__restrict __stacksize) __THROW __nonnull ((1, 2, 3)); /* The following two interfaces are intended to replace the last two. They require setting the address as well as the size since only setting the address will make the implementation on some architectures impossible. */ extern int pthread_attr_setstack (pthread_attr_t *__attr, void *__stackaddr, size_t __stacksize) __THROW __nonnull ((1)); #endif
10、線程的親和性,(需要定義宏__USE_GNU)
#ifdef __USE_GNU /* Thread created with attribute ATTR will be limited to run only on the processors represented in CPUSET. */ extern int pthread_attr_setaffinity_np (pthread_attr_t *__attr, size_t __cpusetsize, const cpu_set_t *__cpuset) __THROW __nonnull ((1, 3)); /* Get bit set in CPUSET representing the processors threads created with ATTR can run on. */ extern int pthread_attr_getaffinity_np (const pthread_attr_t *__attr, size_t __cpusetsize, cpu_set_t *__cpuset) __THROW __nonnull ((1, 3));#endif
