一、執行緒建立基礎
標頭檔與編譯
#include
// 編譯需連結 pthread 庫
gcc -o main main.c -lpthread
// 或
g++ -o main main.cpp -lpthread
pthread_create 原型
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg);
參數詳解
| 參數 | 說明 |
thread | 輸出參數,儲存新建立的執行緒 ID |
attr | 執行緒屬性物件,NULL 表預設屬性 |
start_routine | 執行緒進入函數,單一 void* 參數,回傳 void* |
arg | 傳遞給 start_routine 的參數 |
回傳值
0:成功
- 錯誤碼 (如
EAGAIN, EINVAL, EPERM):失敗,*thread 內容未定義
最小範例
#include
#include
#include
void print_ids(const char *prefix) {
pid_t pid = getpid();
pthread_t tid = pthread_self();
printf("%s pid %u tid %u (0x%x)\n", prefix, (unsigned)pid, (unsigned)tid, (unsigned)tid);
}
void* thread_func(void *arg) {
print_ids("new thread:");
return NULL;
}
int main() {
pthread_t tid;
int err = pthread_create(&tid, NULL, thread_func, NULL);
if (err) {
fprintf(stderr, "create failed: %s\n", strerror(err));
return 1;
}
print_ids("main thread:");
pthread_join(tid, NULL); // 等待子執行緒結束
return 0;
}
二、執行緒同步:pthread_join
int pthread_join(pthread_t thread, void **retval);
- 阻塞呼叫執行緒,直到
thread 結束
retval 接收執行緒回傳值 (start_routine 的 return 值)
- 回收執行緒資源 (joinable 狀態下必須 join 或 detach,否則洩漏)
- 若執行緒已結束,立即返回
三、執行緒分離狀態:Joinable vs Detached
比較表
| 特性 | Joinable (預設) | Detached |
| 資源回收 | 需 pthread_join 回收 | 系統自動回收 |
| 取得回傳值 | 可透過 join 取得 | 不可取得 |
| 同步等待 | 支援 | 不支援 |
| 適用場景 | 需同步、需回傳值的工作 | 獨立背景任務、Fire-and-forget |
設定分離狀態的兩種方式
// 方式 1:建立時透過屬性設定
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, func, arg);
pthread_attr_destroy(&attr);
// 方式 2:執行緒內自我分離
void* func(void *arg) {
pthread_detach(pthread_self());
// ...
}
競態風險與避免
若分離執行緒極快結束,pthread_create 可能尚未回傳執行緒 ID,導致主執行緒取得無效 ID。解決:在子執行緒開頭加短暫延遲 (usleep/nanosleep) 或使用條件變數同步。
四、執行緒屬性進階設定
常用屬性
detachstate:分離狀態 (上述)
stacksize:堆疊大小 (最小 PTHREAD_STACK_MIN,通常 1MB 預設)
guardsize:保護頁大小 (防堆疊溢位)
schedpolicy / schedparam:排程策略與優先權 (需 root 權限)
inheritsched:繼承父執行緒排程屬性
屬性物件生命週期
pthread_attr_t attr;
pthread_attr_init(&attr); // 初始化 (預設值)
// 設定各屬性...
pthread_create(&tid, &attr, func, arg);
pthread_attr_destroy(&attr); // 銷毀,釋放內部資源
五、完整範例:帶參數傳遞、回傳值、錯誤處理
#include
#include
#include
#include
#include
typedef struct {
int id;
char *msg;
} thread_arg_t;
void* worker(void *arg) {
thread_arg_t *ta = (thread_arg_t*)arg;
printf("Thread %d: %s\n", ta->id, ta->msg);
sleep(1);
char *ret = malloc(32);
snprintf(ret, 32, "Result from thread %d", ta->id);
return ret; // 回傳動態分配記憶體
}
int main() {
pthread_t t1, t2;
thread_arg_t a1 = {1, "Hello"}, a2 = {2, "World"};
void *res1, *res2;
if (pthread_create(&t1, NULL, worker, &a1) != 0) return 1;
if (pthread_create(&t2, NULL, worker, &a2) != 0) return 1;
pthread_join(t1, &res1);
pthread_join(t2, &res2);
printf("Joined: %s\n", (char*)res1);
printf("Joined: %s\n", (char*)res2);
free(res1); free(res2);
return 0;
}
常見錯誤與除錯提示
- 忘記
-lpthread:連結錯誤 undefined reference to pthread_create
- 傳遞區域變數指標:主執行緒結束前子執行緒仍在存取 → 傳遞動態記憶體或確保生命週期
- 未 join 也未 detach:執行緒結束後變成「殭屍執行緒」,洩漏資源
- 在執行緒中呼叫
exit():會結束整個進程,請用 pthread_exit() 或 return