linux网络编程-posix信号量与互斥锁(39)

摘要:
函数sem_Init()用于初始化信号量。函数sem_Post用于增加信号量值。函数sem_ Wait用于阻塞当前线程,直到信号量sem值大于0。取消阻塞后,将sem值减小一,表示使用后公共资源减少。

-posix信号量信号量

linux网络编程-posix信号量与互斥锁(39)第1张

linux网络编程-posix信号量与互斥锁(39)第2张

是打开一个有名的信号量

sem_init是打开一个无名的信号量,无名信号量的销毁用sem_destroy

sem_wait和sem_post是对信号量进行pv操作,既可以使用在有名的信号量也可以使用在无名的信号量

 无名信号量是否意味着它不能够用于不同进程的多个线程之间的通信了

linux网络编程-posix信号量与互斥锁(39)第3张

如果sem_init是非零的参数,那么这个无名的信号量可以用于不同进程间的多个线程之间的通信,前提条件是这个信号量的对象必须存储在共享内存区才可以。

linux网络编程-posix信号量与互斥锁(39)第4张

上面这是互斥锁是无名的互斥锁,同样也可以可以用于不同进程间的多个线程之间的通信,前提条件是这个信号量的对象必须存储在共享内存区才可以。

下面我们使用互斥锁来解决生产者消费者的问题

生产者消费者问题是这样描述的,有一个缓冲区,是一个有大小的缓冲区,对于生产者来说,首先判断当前缓冲区是否满了,满了就阻塞不再生产,我们使用信号量来实现

使用信号量p(sem_full)的信号量来实现,初始化的值就是缓冲区的大小,一旦生产了一个产品sem_full的计数值值减一,一旦我们生成了产品,缓冲区不再是空的状态,我们使用一个

v(sem_empty)的信号量,用来告诉消费者可以来消费产品了,我们在生成产品的时候,由于生产者可能是多个,我们需要有一个互斥锁来包含缓冲区。

linux网络编程-posix信号量与互斥锁(39)第5张

对于消费者,消费者也存在多个,首先判断当前缓冲区是否是空的,如果是空的就不能消费产品,需要等待生产者给消费者一个信号,消费者新建一个p(sem_empty)信号,使得信号量加一,这样消费者就可以消费产品了,

一旦消费者消费了一个产品,就会使得缓冲区的大小加一v(sem_full)这样的信号量,对于缓冲区,多个消费者也需要互斥

linux网络编程-posix信号量与互斥锁(39)第6张

对于初始化,一开始只能生产产品,不能消费产品,假设缓冲区的大小是10,sem_full是10,sem_empty为0

linux网络编程-posix信号量与互斥锁(39)第7张

 下面我们通过代码来理解下面的问题:

信号量是在多线程环境中共享资源的计数器

sem_wait() 减小(锁定)由sem指定的信号量的值.如果信号量的值比0大,
  那么进行减一的操作,函数立即返回.
  如果信号量当前为0值,那么调用就会一直阻塞直到或者是信号量变得可以进行减一的操作
  (例如,信号量的值比0大),或者是信号处理程序中断调用

当前初始化的sem_full的值是10,减一之后函数会立即返回不会阻塞

sem_wait   sem_post

信号量的数据类型为结构sem_t,它本质上是一个长整型的数。函数sem_init()用来初始化一个信号量。它的原型为:  

extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));  

sem为指向信号量结构的一个指针;pshared不为0时此信号量在进程间共享,否则只能为当前进程的所有线程共享;value给出了信号量的初始值。  

函数sem_post( sem_t *sem )用来增加信号量的值。当有线程阻塞在这个信号量上时,调用这个函数会使其中的一个线程不在阻塞,选择机制同样是由线程的调度策略决定的。  

函数sem_wait( sem_t *sem )被用来阻塞当前线程直到信号量sem的值大于0,解除阻塞后将sem的值减一,表明公共资源经使用后减少。函数sem_trywait ( sem_t *sem )是函数sem_wait()的非阻塞版本,它直接将信号量sem的值减一。  

函数sem_destroy(sem_t *sem)用来释放信号量sem。 

信号量用sem_init函数创建的,下面是它的说明:
  #include<semaphore.h>
 int sem_init (sem_t *sem, int pshared, unsigned int value);

 生产完一个产品之后sem_post(&sem_empty)是的sem_empty的信号量的值加一

 我们来看程序的代码:

#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define ERR_EXIT(m)
do
{
perror(m);
exit(EXIT_FAILURE);
} while(0)

#define CONSUMERS_COUNT 1
#define PRODUCERS_COUNT 1
#define BUFFSIZE 10

int g_buffer[BUFFSIZE];

unsigned short in = 0;
unsigned short out = 0;
unsigned short produce_id = 0;
unsigned short consume_id = 0;

sem_t g_sem_full;
sem_t g_sem_empty;
pthread_mutex_t g_mutex;

pthread_t g_thread[CONSUMERS_COUNT + PRODUCERS_COUNT];

void *consume(void *arg)
{
int i;
int num = (int)arg;
while (1)
{
printf("%d wait buffer not empty ", num);
sem_wait(&g_sem_empty);
pthread_mutex_lock(&g_mutex);

for (i = 0; i < BUFFSIZE; i++)
{
printf("%02d ", i);
if (g_buffer[i] == -1)
printf("%s", "null");
else
printf("%d", g_buffer[i]);

if (i == out)
printf(" <--consume");

printf(" ");
}
consume_id = g_buffer[out];
printf("%d begin consume product %d ", num, consume_id);
g_buffer[out] = -1;
out = (out + 1) % BUFFSIZE;
printf("%d end consume product %d ", num, consume_id);
pthread_mutex_unlock(&g_mutex);
sem_post(&g_sem_full);
sleep(1);
}
return NULL;
}

void *produce(void *arg)
{
int num = (int)arg;
int i;
while (1)
{
printf("%d wait buffer not full ", num);
sem_wait(&g_sem_full);
pthread_mutex_lock(&g_mutex);
for (i = 0; i < BUFFSIZE; i++)
{
printf("%02d ", i);
if (g_buffer[i] == -1)
printf("%s", "null");
else
printf("%d", g_buffer[i]);

if (i == in)
printf(" <--produce");

printf(" ");
}

printf("%d begin produce product %d ", num, produce_id);
g_buffer[in] = produce_id;
in = (in + 1) % BUFFSIZE;
printf("%d end produce product %d ", num, produce_id++);
pthread_mutex_unlock(&g_mutex);
sem_post(&g_sem_empty);
sleep(5);
}
return NULL;
}

int main(void)
{
int i;
for (i = 0; i < BUFFSIZE; i++)
g_buffer[i] = -1;

sem_init(&g_sem_full, 0, BUFFSIZE);
sem_init(&g_sem_empty, 0, 0);

pthread_mutex_init(&g_mutex, NULL);


for (i = 0; i < CONSUMERS_COUNT; i++)
pthread_create(&g_thread[i], NULL, consume, (void *)i);

for (i = 0; i < PRODUCERS_COUNT; i++)
pthread_create(&g_thread[CONSUMERS_COUNT + i], NULL, produce, (void *)i);

for (i = 0; i < CONSUMERS_COUNT + PRODUCERS_COUNT; i++)
pthread_join(g_thread[i], NULL);

sem_destroy(&g_sem_full);
sem_destroy(&g_sem_empty);
pthread_mutex_destroy(&g_mutex);

return 0;
}

编译的时候需要加上gcc proudct.c -o product -lpthread

可以查看博客:

http://blog.csdn.net/nk_test/article/details/50449704

免责声明:文章转载自《linux网络编程-posix信号量与互斥锁(39)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇裸奔的智能插座:博联Broadlink SP2/SP mini的分析、破解好用的Linux下PDF编辑批注软件PDF Studio Pro 11下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

httpd三种MPM的原理剖析

apache httpd系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.html 本文专讲httpd MPM。为了更完整、权威,我先把apache httpd 2.4关于prefork、worker和event的官方手册大致翻译了一遍,也就是本文的前3节。水平有限,难免翻译的"鬼才看得懂",还请见谅。...

JVM基本讲解

 1.数据类型     java虚拟机中,数据类型可以分为两类:基本类型和引用类型。     基本类型的变量保存原始值,即:它代表的值就是数值本身,而引用类型的变量保存引用值。     “引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。     基本类型包括:byte、short、int、long、char、flo...

Mysql主从同步的实现原理与配置实战

1、什么是mysql主从同步? 当master(主)库的数据发生变化的时候,变化会实时的同步到slave(从)库。 2、主从同步有什么好处? 水平扩展数据库的负载能力。 容错,高可用。Failover(失败切换)/High Availability 数据备份。 3、主从同步的原理是什么? 首先我们来了解master-slave的体系结构。如下图:...

为什么要用Volley中的RequestFuture封装RxJava来用异步请求处理同步请求?

前几天健哥喊我研究一下RvJava,在网络请求用,更简洁更有条理,然后就会抽空研究研究,现在项目里网络库是Volley,就结合项目和网上的demo看,突然脑袋蹦出这个问题,现在看起来这个问题有一点蠢蠢的。 firstly,名词解释一下。 Volley是谷歌爸爸给咱们封装好了的网络请求库,帮我们封装了具体的请求,线程切换以及数据转换,适合短小多并发的网络请求...

WinForm跨线程访问控件异常

WinForm跨线程访问控件异常 最近做了个WinForm的小项目,遇到个简单的问题记录下。需求:点击”下载“,显示正在下载,下载完后更新状态为”ready“(要求用多线程,避免前台卡死)。 做法 新建一个线程,在新线程中下在更新控件状态,报异常跨线程程访问控件(当然可以通过关闭跨线程访问控件检验解决,不鼓励这么做)。应该C#5.0版本添加的任务Task异...

c++中CreateEvent函数

参考:https://blog.csdn.net/u011642774/article/details/52789969 函数原型: [cpp] view plain copy  HANDLE CreateEvent(     LPSECURITY_ATTRIBUTES lpEventAttributes, // SD     BOOL bMan...