文章目錄
- 一、 線程的同步
- (一)無名信號量sem
- 1. 定義和初始化
- 2.獲取信號量
- 3.釋放信號量
- 4. 銷毀
- 5. 使用示例
- (二)條件變量
- 1. 定義和初始化
- 2. 獲取條件變量
- 3. 釋放條件變量
- 4. 銷毀條件變量
- 二、進程間通信
- (一)無名管道
- 1.概念
- 2. 定義
- 3. 特點
- (二)有名管道
- 1. 原理
- 2. 定義
- 3. 特點
- (三)信號通信
- 1. 概念
- 2. 定義
一、 線程的同步
線程同步:提前已經知道了線程應該有的執行的順序,控制線程按指定的順序執行
互斥鎖無法保證線程執行的順序,一個線程解鎖后無法保證是另一個線程上鎖,有可能仍是原來剛解鎖的線程又再次上鎖
無名信號量是荷蘭計算器科學家發明的,PV操作的PV來自荷蘭語。
(一)無名信號量sem
1. 定義和初始化
#include <semaphore.h>定義無名信號量sem_t sem;
初始化無名信號量int sem_init(sem_t *sem, int pshared, unsigned int value);功能:初始化無名信號量參數:sem:無名信號量指針pshared:0:兩個線程間同步非0:兩個進程間同步value:信號量的初始值如果是1表示可以獲取信號量如果是0表示不可以獲取信號量返回值:成功 0失敗 -1 重置錯誤碼
2.獲取信號量
獲取信號量(P操作)int sem_wait(sem_t *sem);功能:獲取信號量 (將信號量的值-1)如果信號量的值已經是0了,則sem_wait會阻塞,等到能執行減1操作為止參數:sem:無名信號量指針返回值:成功 0失敗 不會改變信號量的值 返回 -1 重置錯誤碼
3.釋放信號量
釋放信號量(V操作)int sem_post(sem_t *sem);功能:釋放信號量(將信號量的值+1)參數:sem:無名信號量指針返回值:成功 0失敗 不會改變信號量的值 返回 -1 重置錯誤碼
4. 銷毀
銷毀無名信號量int sem_destroy(sem_t *sem);功能:銷毀無名信號量參數:sem:無名信號量指針返回值:成功 0
- 注:
- 無名信號量不允許減到小于1,當等于0時,sem_wait會阻塞等待;
- 但是無名信號量允許加到大于1
5. 使用示例
現有三個線程,其功能分別為打印A、打印B、打印C,使用無名信號量使其按照ABC的順序打印。
#include <my_head.h>sem_t sem1;
sem_t sem2;
sem_t sem3;//A線程
void *task_func_1(void *arg){while(1){sem_wait(&sem1);sleep(1);printf("A ");fflush(stdout);sem_post(&sem2);}
}//B線程
void *task_func_2(void *arg){while(1){sem_wait(&sem2);sleep(1);printf("B ");fflush(stdout);sem_post(&sem3);}
}
//C線程
void *task_func_3(void *arg){while(1){sem_wait(&sem3);sleep(1);printf("C ");fflush(stdout);sem_post(&sem1);}
}int main(int argc, const char *argv[])
{//初始化無名信號量sem_init(&sem1, 0, 1);sem_init(&sem2, 0, 0);sem_init(&sem3, 0, 0);pthread_t tid1 = 0, tid2 = 0, tid3=0;int ret = 0;if(0 != (ret = pthread_create(&tid1, NULL, task_func_1, NULL))){printf("pthread_create error : %s\n", strerror(ret));exit(-1);}if(0 != (ret = pthread_create(&tid2, NULL, task_func_2, NULL))){printf("pthread_create error : %s\n", strerror(ret));exit(-1);}if(0 != (ret = pthread_create(&tid3, NULL, task_func_3, NULL))){printf("pthread_create error : %s\n", strerror(ret));exit(-1);}pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_join(tid3, NULL);//銷毀無名信號量sem_destroy(&sem1);sem_destroy(&sem2);sem_destroy(&sem3);return 0;
}
(二)條件變量
無名信號量適合線程數比較少的線程中實現微觀的同步過程,
條件變量更適用于大量線程實現同步
1. 定義和初始化
#include <pthread.h>pthread_cond_t cond;pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//靜態初始化
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr);
功能:動態初始化條件變量
參數:cond:條件變量指針cond_attr:條件變量的屬性 NULL 表示使用默認屬性
返回值:成功 0 失敗 錯誤碼
2. 獲取條件變量
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
功能:獲取條件變量
參數:cond:條件變量指針mutex:互斥鎖指針
返回值:成功 0 失敗 錯誤碼
使用流程:1.先獲取到互斥鎖(可以先完成一些任務初始化的工作)2.調用pthread_cond_wait2.1 將當前線程添加到隊列中2.2 解鎖2.3 在隊列中休眠2.4 重新獲取鎖這時,如果等待的條件沒有發生,會繼續解鎖、休眠如果等待的條件發生了,會將線程在隊列中移除3.執行后續的任務4.解鎖
3. 釋放條件變量
int pthread_cond_signal(pthread_cond_t *cond);
功能:釋放一個條件變量,喚醒一個等待條件的線程
參數:cond:條件變量指針
返回值:成功 0 失敗 錯誤碼int pthread_cond_broadcast(pthread_cond_t *cond);
功能:釋放所有的資源,喚醒所有等待條件的線程
參數:cond:條件變量指針
返回值:成功 0 失敗 錯誤碼
4. 銷毀條件變量
int pthread_cond_destroy(pthread_cond_t *cond);
功能:銷毀條件變量
參數:cond:條件變量指針
返回值:成功 0 失敗 錯誤碼
二、進程間通信
進程間通信方式
1.傳統進程間通信
無名管道
有名管道
信號通信
2.system V 版本引入了IPC進程間通信
消息隊列
共享內存
信號燈集
socket套接字通信
(一)無名管道
1.概念
在使用fork函數創建子進程前打開的文件描述符,在fork之后,子進程會繼承父進程打開的文件描述符。
無名管道是內核空間實現的機制,只能用于親緣進程間通信,無名管道的大小是64K。
2. 定義
#include <unistd.h>
int pipe(int pipefd[2]);
功能:創建一個管道 一個單向的數據通道 可用于進程間通信數組 pipefd 中會返回兩個文件描述符,pipefd[0] 管道的讀端 pipefd[1] 管道的寫端寫入管道的數據會被內核緩沖 直到被讀走
參數:pipefd:保存管道的兩個端點的文件描述符的數據
返回值:成功 0失敗 -1 重置錯誤碼
3. 特點
1.只能用于親緣間進程的通信
2.無名管道數據半雙工的通信的方式
單工 : A -------------->B
半雙工 : 同一時刻 A----->B B------>A
全雙工 : 同一時刻 A<---->B
3.無名管道的大小是64K
4.無名管道不能夠使用lseek函數(調用會出錯 返回 -1)
5.讀寫的特點
如果讀端存在 寫管道:有多少寫多少,直到寫滿為止(64k)寫阻塞,
直到管道中騰出新的4K空間,寫操作解除阻塞
如果讀端不存在 寫管道,管道破裂(SIGPIPE)
如果寫端存在 讀管道:有多少讀多少,沒有數據的時候阻塞等待
如果寫端不存在 讀管道:有多少讀多少,沒有數據的時候立即返回(非阻塞)
(二)有名管道
1. 原理
有名管道會在文件系統中創建一個管道文件,只需要打開這個文件,進行讀寫操作即可。
有名管道是在文件系統中映射出一個管道文件名,管道文件本質是在內存上的,在硬盤上的只是一個標識。
2. 定義
mkfifo命令也可以創建管道文件
管道文件不能重名
在管道文件中寫入內容,文件大小并不會增加,因為寫入管道文件的內容保存在內存中,并非硬盤中
//也可以在終端上使用 mkfifo 命令創建管道文件
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:
參數:pathname:管道文件的路徑和名字mode:權限 & ~umask --》 最終權限
返回值:成功 0失敗 -1 重置錯誤碼
3. 特點
- 可以用于任意進程間的通信,不僅限親緣進程
- 有名管道數據是半雙工的通信方式
- 有名管道的大小是64K
- 有名管道不能夠使用lseek函數(調用會失敗 返回 -1)
- 讀寫的特點
如果讀端存在寫管道:有多少寫多少,直到寫滿為止(64k)寫阻塞
如果讀端不存在寫管道
1.讀端沒有打開,寫端在open的位置阻塞
2.讀端打開后關閉,管道破裂(SIGPIPE)
如果寫端存在讀管道:有多少讀多少,沒有數據的時候阻塞等待
如果寫端不存在讀管道
1.寫端沒有打開,讀端在open的位置阻塞
2.寫端打開后關閉,有多少讀多少,沒有數據的時候立即返回
(三)信號通信
1. 概念
信號是中斷的一種軟件模擬,中斷是基于硬件的概念,信號是基于linux內核實現的。
用戶可以給進程發信號,進程可以給進程發信號,linux內核也可以給進程發信號。
信號的處理方式有三種:默認DEF、忽略IGN、捕捉 caught
man 7 signal
信號查看:kill -l
2. 定義
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:注冊信號和信號處理方式的關系
參數:signum:信號的編號handler:處理方式SIG_IGN 忽略SIG_DFL 默認也可以傳一個函數 捕捉void sig_func(int signum){//自定義的邏輯}
返回值:成功 返回handler失敗 SIG_ERR 重置錯誤碼
signal函數只是注冊了信號和處理方式的關系,并不會阻塞等待信號產生