嵌入式Linux并发程序设计,线程,线程间通信--同步,线号量,信号量初始化sem_init(),P
2019-07-12 21:58发布
生成海报
文章目录
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
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
int sem_wait(sem_t *sem); P操作
int sem_post(sem_t *sem); V操作
·成功时返回0,失败时返回EOF
·sem 指向要操作的信号量对象
7,线程同步—示例1
两个线程同步读写缓冲区(生产者/消费者问题)
#include
#include
#include
#include
#include
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
");
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
", 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
- 多了两个线程,线程号LWP分别是4464、4465
- 他们同属于一个进程,进程号PID是4464
- 两个线程都处于等待态
程序功能如下所示
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
两个线程同步读写缓冲区(生产者/消费者问题)
上面的示例并没有实现严格意义上的同步
- 读线程在读缓冲区前,P操作检查缓冲区中有没有数据,没有的话阻塞,有的话才读数据
- 实际上对于写线程来说,也要如此。当缓冲区为空的时候,才能去写数据。
- 如果读线程数据处理过程比较长,读线程还没读完,写线程又把新数据覆盖上去了,这样就破坏了数据,是不合理的
#include
#include
#include
#include
#include
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
");
do {
sem_wait(&sem_w);
fgets(buf,32,stdin);
sem_post(&sem_r);
}while (strncmp(buf,"quit",4) != 0);
return 0;
}
void *function(void *arg)
{
while (1)
{
sem_wait(&sem_r);
printf("you enter %d characters
",strlen(buf));
sem_post(&sem_w);
}
}
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮