天天看點

嵌入式Linux并發程式設計,線程,線程間通信--同步,線号量,信号量初始化sem_init(),P/V操作sem_wait()/sem_post()1,線程間通信2,線程通信–同步3,信号量(燈)4,Posix信号量5,信号量初始化sem_init()6,信号量–P/V操作sem_wait()/sem_post()7,線程同步—示例18,線程同步—示例2

文章目錄

  • 1,線程間通信
  • 2,線程通信--同步
  • 3,信号量(燈)
  • 4,Posix信号量
  • 5,信号量初始化sem_init()
  • 6,信号量--P/V操作sem_wait()/sem_post()
  • 7,線程同步---示例1
  • 8,線程同步---示例2

1,線程間通信

  1. 線程共享同一程序的位址空間
  2. 優點:線程間通信很容易通過全局變量交換資料
  3. 缺點:多個線程通路共享資料時需要同步或互斥機制

2,線程通信–同步

  1. 同步(synchronization)指的是多個任務按照約定的先後次序互相配合完成一件事情
  2. 1968年,Edsgar Dijkstra基于信号量的概念提出了一種同步機制
  3. 由信号量來決定線程是繼續運作還是阻塞等待

3,信号量(燈)

  1. 信号量代表某一類資源,其值表示系統中該資源的數量
  2. 信号量是一個受保護的變量,隻能通過三種操作來通路

    ·初始化

    ·P操作(申請資源):當任務(比如線程)要通路某個資源的時候,因為任務不知道目前系統中有沒有這個資源,是以該任務對代表此資源的信号量進行P操作(檢查信号量的值):如果信号量的值大于0,任務繼續執行,通路資源,如果目前信号量的值等于0,就代表沒有資源,則任務阻塞,直到有資源為止。

    ·V操作(釋放資源):如果目前任務不需要通路資源了,或者任務産生了一個資源,就要執行V操作(告訴系統,資源數增加了,系統就可以喚醒等待這些資源的任務了)

4,Posix信号量

  1. posix中定義了兩類信号量:

    ·無名信号量(基于記憶體的信号量):僅記憶體中存在,沒有實際的檔案和信号量一一對應,主要用于程序内部線程之間通信(,也可以用于程序之間但不友善)

    ·有名信号量:可用于線程間通信,也可用于程序間通信

  2. 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);

  1. 成功時傳回0,失敗時EOF
  2. sem 指向要初始化的信号量對象
  3. pshared 0 – 線程間 1 – 程序間
  4. val 信号量初值

6,信号量–P/V操作sem_wait()/sem_post()

  1. P(S) 含義如下:
if  (信号量的值大于0) 
{   
	申請資源的任務繼續運作;
	信号量的值減一;}
else 
{
	申請資源的任務阻塞;
} 
           
  1. 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
           
  1. 多了兩個線程,線程号LWP分别是4464、4465
  2. 他們同屬于一個程序,程序号PID是4464
  3. 兩個線程都處于等待态
程式功能如下所示
[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

兩個線程同步讀寫緩沖區(生産者/消費者問題)

上面的示例并沒有實作嚴格意義上的同步
  1. 讀線程在讀緩沖區前,P操作檢查緩沖區中有沒有資料,沒有的話阻塞,有的話才讀資料
  2. 實際上對于寫線程來說,也要如此。當緩沖區為空的時候,才能去寫資料。
  3. 如果讀線程資料處理過程比較長,讀線程還沒讀完,寫線程又把新資料覆寫上去了,這樣就破壞了資料,是不合理的
#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);
	} 
}
           

繼續閱讀