文章目錄
- 1,線程間通信
- 2,線程通信--同步
- 3,信号量(燈)
- 4,Posix信号量
- 5,信号量初始化sem_init()
- 6,信号量--P/V操作sem_wait()/sem_post()
- 7,線程同步---示例1
- 8,線程同步---示例2
1,線程間通信
- 線程共享同一程序的位址空間
- 優點:線程間通信很容易通過全局變量交換資料
- 缺點:多個線程通路共享資料時需要同步或互斥機制
2,線程通信–同步
- 同步(synchronization)指的是多個任務按照約定的先後次序互相配合完成一件事情
- 1968年,Edsgar Dijkstra基于信号量的概念提出了一種同步機制
- 由信号量來決定線程是繼續運作還是阻塞等待
3,信号量(燈)
- 信号量代表某一類資源,其值表示系統中該資源的數量
-
信号量是一個受保護的變量,隻能通過三種操作來通路
·初始化
·P操作(申請資源):當任務(比如線程)要通路某個資源的時候,因為任務不知道目前系統中有沒有這個資源,是以該任務對代表此資源的信号量進行P操作(檢查信号量的值):如果信号量的值大于0,任務繼續執行,通路資源,如果目前信号量的值等于0,就代表沒有資源,則任務阻塞,直到有資源為止。
·V操作(釋放資源):如果目前任務不需要通路資源了,或者任務産生了一個資源,就要執行V操作(告訴系統,資源數增加了,系統就可以喚醒等待這些資源的任務了)
4,Posix信号量
-
posix中定義了兩類信号量:
·無名信号量(基于記憶體的信号量):僅記憶體中存在,沒有實際的檔案和信号量一一對應,主要用于程序内部線程之間通信(,也可以用于程序之間但不友善)
·有名信号量:可用于線程間通信,也可用于程序間通信
-
pthread庫常用的信号量操作函數如下:
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem); // P操作
int sem_post(sem_t *sem); // V操作
5,信号量初始化sem_init()
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int val);
- 成功時傳回0,失敗時EOF
- sem 指向要初始化的信号量對象
- pshared 0 – 線程間 1 – 程序間
- val 信号量初值
6,信号量–P/V操作sem_wait()/sem_post()
- P(S) 含義如下:
if (信号量的值大于0)
{
申請資源的任務繼續運作;
信号量的值減一;}
else
{
申請資源的任務阻塞;
}
- V(S) 含義如下:
信号量的值加一;
if (有任務在等待資源)
{
喚醒等待的任務,讓其繼續運作
}
#include <semaphore.h>
int sem_wait(sem_t *sem); P操作
int sem_post(sem_t *sem); V操作
·成功時傳回0,失敗時傳回EOF
·sem 指向要操作的信号量對象
7,線程同步—示例1
兩個線程同步讀寫緩沖區(生産者/消費者問題)
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
char buf[32];
sem_t sem;
void *function(void *arg);
int main(void)
{
pthread_t a_thread;
if (sem_init(&sem,0,0) < 0) //先初始化信号量,再建立線程
{
perror("sem_init");
exit(-1);
}
if (pthread_create(&a_thread,NULL,function,NULL) != 0) //先初始化信号量,再建立線程
{
printf("fail to pthread_create");
exit(-1);
}
printf("input ‘quit’ to exit\n");
do {
fgets(buf,32,stdin);
sem_post(&sem);
}while (strncmp(buf,"quit",4) != 0);
return 0;
}
void *function(void *arg)
{
while (1)
{
sem_wait(&sem);
printf("you enter %d characters\n", strlen(buf));
}
}
檢視線程要加"-L"
[email protected]:~/test/pthread$ ps aux -L |grep sem
linux 2978 2978 0.0 1 0.0 14772 928 pts/11 T 15:05 0:00 vi semc.c
linux 4104 4104 0.0 1 0.5 14636 5488 pts/11 S+ 16:53 0:00 vi sem.c
linux 4463 4463 0.0 1 0.0 6112 852 pts/14 S+ 17:28 0:00 grep --color=auto sem
[email protected]:~/test/pthread$ ./sem.out
input ‘quit’ to exit
再打開一個終端
[email protected]:~/test/pthread$ ps aux -L |grep sem
linux 2978 2978 0.0 1 0.0 14772 928 pts/11 T 15:05 0:00 vi semc.c
linux 4104 4104 0.0 1 0.5 14636 5488 pts/11 S+ 16:53 0:00 vi sem.c
linux 4464 4464 0.0 2 0.0 10476 308 pts/14 Sl+ 17:28 0:00 ./sem.out
linux 4464 4465 0.0 2 0.0 10476 308 pts/14 Sl+ 17:28 0:00 ./sem.out
linux 4473 4473 0.0 1 0.0 6112 856 pts/0 S+ 17:29 0:00 grep --color=auto sem
- 多了兩個線程,線程号LWP分别是4464、4465
- 他們同屬于一個程序,程序号PID是4464
- 兩個線程都處于等待态
程式功能如下所示
[email protected]:~/test/pthread$ ./sem.out
input ‘quit’ to exit
qw
you enter 3 characters
q
you enter 2 characters
^C
[email protected]:~/test/pthread$
8,線程同步—示例2
兩個線程同步讀寫緩沖區(生産者/消費者問題)
上面的示例并沒有實作嚴格意義上的同步
- 讀線程在讀緩沖區前,P操作檢查緩沖區中有沒有資料,沒有的話阻塞,有的話才讀資料
- 實際上對于寫線程來說,也要如此。當緩沖區為空的時候,才能去寫資料。
- 如果讀線程資料處理過程比較長,讀線程還沒讀完,寫線程又把新資料覆寫上去了,這樣就破壞了資料,是不合理的
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
char buf[32];
sem_t sem_r,sem_w;
void *function(void *arg);
int main(void)
{
pthread_t a_thread;
if (sem_init(&sem_r,0,0) < 0) //剛開始緩沖區是空的,不可讀
{
perror("sem_r_init");
exit(-1);
}
if (sem_init(&sem_w,0,1) < 0) //剛開始緩沖區是空的,可寫
{
perror("sem_r_init");
exit(-1);
}
if (pthread_create(&a_thread,NULL,function,NULL) != 0) //先初始化信号量,再建立線程
{
printf("fail to pthread_create");
exit(-1);
}
printf("input ‘quit’ to exit\n");
do {
sem_wait(&sem_w);//寫之前對可寫信号量進行P操作,緩沖區非空則阻塞
fgets(buf,32,stdin);//緩沖區可寫,寫線程執行寫操作
sem_post(&sem_r); //寫完之後對可讀信号量進行V操作,表示可讀信号量增加了
}while (strncmp(buf,"quit",4) != 0);
return 0;
}
void *function(void *arg)
{
while (1)
{
sem_wait(&sem_r);
printf("you enter %d characters\n",strlen(buf));
sem_post(&sem_w);
}
}