線程是獨立調度的基本單位。一個進程可以有一個或者多個線程,線程之間共享進程資源。
線程分類
按調度者分爲:
-
用戶級線程(User Level Thread,ULT)
解決上下文切換問題,調度算法和過程由用戶決定 存在於用戶空間 線程創建、撤銷以及線程之間的同步、通信無需系統調用來實現 同一進程的線程切換不需要內核支持 調度以進程爲單位
優點:
線程切換不需要到內核空間,節省內核空間 調度算法可以進程專用,亦或用戶程序指定 用戶線程實現與操作系統無關
缺點:
同一進程一個線程執行系統調用阻塞就導致進程阻塞 一個進程只能在一個CPU上獲得執行,無法發揮SMP(對稱多處理器)的優勢
-
核心級線程(kernel Supported threads,KST)
不同進程的線程按照同一相對優先調度算法調度,發揮SMP優勢 線程創建撤銷、以及切換等等通過系統調用陷入內核由內核響應程序處理 調度以線程爲基本單位
優點:
在SMP情況下,內核可以調度同一進程通中的多個線程併發工作 同一線程的一個線程阻塞,其他線程依然可以運行
缺點:
主要是程序在內核態和用戶態之間切換帶來的開銷
線程相關API
線程的創建:
pthread_create - create a new thread //創建一個新線程
//頭文件
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
thread —— 新線程TID(Thread id 類似進程的pid)
attr —— 線程屬性,爲NULL創建一個標準線程
start_routine —— 線程入口函數
arg —— 線程入口參數
RETURN VALUE
成功返回0; 錯誤返回errno, and the contents of *thread are undefined.
errno
—— 一個全局變量,存放各種API執行失敗的原因
線程的退出:
- 線程入口函數調用return語句並指定返回值
- 線程調用
pthread_exit()
- 調用
pthread_cancel
取消線程 - 任意線程調用
exit()
或者main線程
調用了return
,都將導致所有線程退出
pthread_exit - terminate calling thread
#include <pthread.h>
void pthread_exit(void *retval); //線程退出
retval —— 退出值
Compile and link with -pthread.//編譯要鏈接pthread庫
線程取消:
pthread_cancel - send a cancellation request to a thread //發送取消線程請求
#include <pthread.h>
int pthread_cancel(pthread_t thread); //取消線程
RETURN VALUE
成功 pthread_cancel() 返回 0; 失敗, 返回非零 error number.
獲取線程的退出值
pthread_join - join with a terminated thread
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval); //獲取線程退出值
thread —— 線程TID
retval —— 線程退出值
RETURN VALUE
成功 pthread_join() 0; 失敗返回 error number.
獲取自身TID:
pthread_self - obtain ID of the calling thread
#include <pthread.h>
pthread_t pthread_self(void);
RETURN VALUE
成功返回線程TID,不存在失敗
創建標準線程:
/***************************************************************************************************
* @file: xxx.c
* @author: guangjieMVP
* @github:
* @version: v1.0.0
* @date: 2020-xx-xx
* @brief:
*****************************************************************************************************/
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
int retval = 0xff;
void *thread1_entry(void *arg)
{
static int cnt = 0;
while (1)
{
printf("Linux %d\r\n", cnt++);
if(cnt >= 10)
{
cnt = 0;
pthread_exit(&retval);
}
sleep(2);
}
}
void *thread2_entry(void *arg)
{
while (1)
{
printf("ubuntu\r\n");
sleep(3);
}
}
int main(int argc, char **argv)
{
pthread_t thread1;
pthread_t thread2;
pthread_create(&thread1, NULL, thread1_entry, NULL); //attr爲NULL——使用默認屬性創建thread
pthread_create(&thread2, NULL, thread2_entry, NULL);
static int maincnt = 0;
unsigned char flag = 0;
while(1)
{
printf("main thread %d\r\n", maincnt++);
if(maincnt >= 10 && !flag)
{
maincnt = 0;
flag = 1;
pthread_cancel(thread2);
}
sleep(1);
}
return 0;
}
編譯執行:
gcc pthread_create.c -o pthread_create -lpthread //編譯程序
//-lpthread —— 鏈接pthread線程庫
./pthread_create //執行程序
線程分離與接合
默認情況下線程是可連接狀態——線程退出時不會釋放資源,其他線程可以通過pthread_join()
獲取其退出值
不關心線程返回狀態可以把線程設置爲可分離的——線程結束系統自動釋放並清理資源,線程必定不會成爲殭屍線程
s
pthread_detach - detach a thread //設置線程屬性爲分離
#include <pthread.h>
int pthread_detach(pthread_t thread);
thread —— 線程ID
RETURN VALUE
成功返回0; 失敗, 返回errno
線程屬性
線程屬性包含屬性:線程棧的大小和位置、調度策略、優先級等以及線程是分離還是可連接
pthread_attr_init, pthread_attr_destroy - initialize and destroy thread attributes object //初始化或者銷燬銷燬線程屬性
頭文件
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr); //初始化線程屬性
int pthread_attr_destroy(pthread_attr_t *attr);//銷燬線程屬性
attr —— 線程屬性
pthread_attr_setdetachstate, pthread_attr_getdetach‐state - 設置or獲取線程的分離狀態
#include <pthread.h>
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
attr —— 線程屬性
detachstate —— 分離狀態:
PTHREAD_CREATE_DETACHED — 分離狀態
PTHREAD_CREATE_JOINABLE — 可連接狀態
RETURN VALUE
成功返回0; 失敗返回非0的errno
分離屬性創建線程:
int ret;
pthread_attr_t attr; //定義線程屬性變量
ret = pthread_attr_init(&attr); //初始化線程屬性變量
if(ret != 0)
{
printf("faild to init attr\r\n");
exit(0);
}
ret = pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED);//設置線程爲分離狀態
if(ret != 0)
{
printf("faild to setdetach\r\n");
exit(0);
}
pthread_create(&thread1, &attr, thread1_entry, NULL); //使用attr屬性創建線程
pthread_create(&thread2, &attr, thread2_entry, NULL);
設置以及獲取線程調度策略
pthread_attr_setschedpolicy, pthread_attr_getschedpolicy - 獲取or設置線程調度策略
#include <pthread.h>
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); //設置線程調度策略
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
attr —— 線程屬性
policy —— 調度策略
SCHED_FIFO —— 以先進先出的派對方式調度,處於就緒就會被立即放入所在優先級隊列隊尾
被更高優先級搶佔後會被放入所在優先級隊列隊頭,高優先級運行結束其立即
恢復運行
可以調用sched_yield()主動讓出CPU
SCHED_RR —— 以輪轉的方式調度, 線程會被分配一定的時間片,
時間片耗盡會被放入所在優先級隊列的隊尾
SCHED_OTHER —— 非實時調度的普通線程,靜態優先級必須設置爲0,使用Linux系統默認的調度策略——按照動態優先級調度
RETURN VALUE
On success, these functions return 0; on error, they return a nonzero error number.
獲取、設置線程調度策略以及靜態優先級
pthread_attr_setschedparam, pthread_attr_getschedparam - 設置、獲取靜態線程優先級
#include <pthread.h>
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
//attr —— 線程屬性
//param —— 線程靜態優先級
RETURN VALUE
On success, these functions return 0; on error, they return a nonzero error number.
獲取、設置線程調度策略以及靜態優先級
pthread_setschedparam, pthread_getschedparam - 設置or獲取線程調度策略以及靜態優先級
#include <pthread.h>
int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param); //設置調度策略以及靜態優先級
int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param);
//thread —— 線程TID
//policy —— 調度策略
//param —— 靜態優先級
struct sched_param {
int sched_priority; /* Scheduling priority */
};
獲取、設置線程動態優先級
nice - change process priority
#include <unistd.h>
int nice(int inc);
線程棧
線程創建後都有一個線程棧,缺省情況下大小固定。線程棧大小可以改變
#include <pthread.h>
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
attr —— 線程屬性結構體
stackaddr —— 線程棧地址
stacksize —— 線程棧大小
RETURN VALUE
成功返回0;失敗返回非零錯誤編碼