嵌入式Linux并发程序设计,线程,线程间通信--同步,线号量,信号量初始化sem_init(),P/V操作sem_wait()/sem_post()

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"

linux@linux:~/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
linux@linux:~/test/pthread$ ./sem.out 
input ‘quit’ to exit

再打开一个终端

linux@linux:~/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. 两个线程都处于等待态

程序功能如下所示

linux@linux:~/test/pthread$ ./sem.out 
input ‘quit’ to exit
qw
you enter 3 characters
q
you enter 2 characters
^C
linux@linux:~/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);
	} 
}

猜你喜欢

转载自blog.csdn.net/m0_37542524/article/details/83859808