线程间同步之信号量实现环形buf




一.概述:

信号量是一个非负整数的计数器,它通过计数器来实现多线程对临界资源的顺序访问,从而实现线程间的同步。它与进程间通信的信号量不同,进程间通信的信号量是一个信号量集,而线程间同步的信号量是一个信号。还有一点,就是对信号量的操作是原子的。

信号量与互斥锁的区别:

(1).互斥锁的值只能是0或1,而信号量的值为非负整数。

(2).互斥锁用与实现线程间的互斥,而信号量用于实现线程间的同步。

(3).互斥锁的加锁和解锁必须由同一个线程分别对应使用,而信号量可以由一个线程得到,另一个线程释放。

下面是在一个环形buf中实现消费者与生产者模型。在这个实现中,要定义两个信号量,一个信号量表示可以读的数据,一个表示可以写的格子,并且只实现了消费者与生产者的同步,没有实现互斥,即生产者和消费者可以同时操作。如果是多生产者与多消费者,则要实现生产者与生产者之间的互斥,消费者与消费者之间的互斥。




二.相关函数:

(1).sem_init函数:int  sem_init(sem_t  *sem, int  pshared,  unsigned int value)

pshared参数:pshared 参数指明信号量是由进程内线程共享,还是由进程之间共享。如果 pshared 的值为 0,那么信号量将被进程内的线程共享,并且应该放置在所有线程都可见的地址上(如全局变量,或者堆上动态分配的变量)。如果 pshared 是非零值,那么信号量将在进程之间共享,并且应该定位共享内存区域

value参数:初始化信号量的值。

返回值:成功时返回 0;错误时,返回 -1,并把 errno 设置为合适的值。

(2).sem_destroy函数:int  sem_destroy(sem_t  *sem)

函数功能:销毁一个信号量。

返回值:成功时返回 0;错误时,返回 -1,并把 errno 设置为合适的值。

(3).sem_wait函数:int sem_wait(sem_t *sem)

函数功能:获得资源,相当与P操作,将信号量减1。(阻塞式的,并且它的操作是原子的)

返回值:成功时都返回 0;错误保持信号量值没有更改,-1 被返回,并设置 errno 来指明错误。

(4).sem_trywait函数:int sem_trywait(sem_t *sem)

函数功能:获得资源,相当与P操作,将信号量减1。(非阻塞式的,并且它的操作是原子的)

返回值:成功时都返回 0;错误保持信号量值没有更改,-1 被返回,并设置 errno 来指明错误。

(5).sem_post函数:int sem_post(sem_t *sem)

函数功能:释放资源,相当与V操作,将信号量加1,并唤醒挂起等待该资源的线程。

返回值:成功时都返回 0;错误保持信号量值没有更改,-1 被返回,并设置 errno 来指明错误。




三.相关代码:

单线程情况:

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<pthread.h>
  4 #include<semaphore.h>
  5 
  6 #define SIZE 20
  7 const int BLANK_INIT = 20;//定义环形buf一共有20个格子
  8 const int DATA_INIT = 0;      //开始可以消费的数据为0                                                                                                                                 
  9 sem_t blank;
 10 sem_t data;
 11 int buf[SIZE] = {0};
 12 pthread_t tid1, tid2;
 13 
 14 void* product(void *arg)
 15 {
 16     int num = 1;
 17     int index = 0;
 18     
 19     while(1)
 20     {   
 21         sem_wait(&blank);      //获得格子资源
 22         buf[index++] = num;
 23         index = index % SIZE;
 24         printf("product data is : %d\n", num);
 25         num++;
 26         sem_post(&data);       //释放数据资源
 27     }
 28     return NULL;
 29 }
 30 
 31 void* consumer(void* arg)
 32 {
 33     int index = 0;
 34     int num = 0;
 35     
 36     while(1)
 37     {   
 38         sem_wait(&data);      //获得数据资源
 39         num = buf[index++];
 40         index = index % SIZE;
 41         printf("consumer data is : %d\n", num);
 42         sleep(1);
 43         sem_post(&blank);     //释放格子资源
 44     }
 45 }
 46 
 47 void run_product_consumer()
 48 {
 49     pthread_create(&tid1, NULL, product, NULL);
 50     pthread_create(&tid1, NULL, consumer, NULL);
 51 
 52     pthread_join(tid1, NULL);
 53     pthread_join(tid1, NULL);
 54 }
 55 
 56 void init_allsem()
 57 {
 58     if( sem_init(&blank, 0, BLANK_INIT) < 0)
 59     {
 60         printf("init product error....\n");
 61     }
 62     if( sem_init(&data, 0, DATA_INIT) < 0)
 63     {
 64         printf("init consumer error....\n");
 65     }
 66 }
 67 
 68 void destroy_allsem()
 69 {
 70     if( sem_destroy(&blank) < 0)
 71     {
 72         printf("destroy blank sem error...\n");
 73     }
 74     if( sem_destroy(&data) < 0)
 75     {
 76         printf("destroy data sem error...\n");
 77     }
 78 }
 79 
 80 
 81 
 82 int main()
 83 {
 84     init_allsem();
 85     run_product_consumer();
 86     destroy_alsem();
 87     return 0;
 88 }

执行结果:

多线程情况:

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<pthread.h>
  4 #include<semaphore.h>
  5 #include<sys/types.h>
  6 
  7 #define SIZE 20
  8 const int BLANK_INIT = 20;
  9 const int DATA_INIT = 0;
 10 sem_t blank;
 11 sem_t data;
 12 pthread_mutex_t lock;
 13 int buf[SIZE] = {0};
 14 pthread_t tid1, tid2, tid3, tid4;
 15 
 16 void* product(void *arg)
 17 {
 18     int num = 1;
 19     int index = 0;
 20 
 21     while(1)
 22     {
 23         sem_wait(&blank);      //获得格子资源                                                                                                                  
 24         pthread_mutex_lock(&lock);//多线程加的代码
 25         buf[index++] = num;
 26         index = index % SIZE;
 27         printf("tid is : %lu   product data is : %d\n",pthread_self(), num);
 28         num++;
 29         pthread_mutex_unlock(&lock);//多线程加的代码
 30         sleep(2);              //改
 31         sem_post(&data);       //释放数据资源
 32     }
 33     return NULL;
 34 }
 35 
 36 void* consumer(void* arg)
 37 {
 38     int index = 0;
 39     int num = 0;
 40 
 41     while(1)
 42     {
 43         sem_wait(&data);      //获得数据资源
 44         pthread_mutex_lock(&lock);//多线程加的代码
 45         num = buf[index++];
 46         index = index % SIZE;
 47         printf("tid is :%lu   consumer data is : %d\n",pthread_self(), num);
 48         pthread_mutex_unlock(&lock);//多线程加的代码
 49         sleep(1);
 50         sem_post(&blank);     //释放格子资源
 51     }
 52 }
 53 
 54 void run_product_consumer()
 55 {
 56     pthread_create(&tid1, NULL, product, NULL);
 57     pthread_create(&tid2, NULL, product, NULL);
 58     pthread_create(&tid3, NULL, consumer, NULL);
 59     pthread_create(&tid4, NULL, consumer, NULL);
 60 
 61     pthread_join(tid1, NULL);
 62     pthread_join(tid1, NULL);
 63 }
 64 
 65 void init_allsem()
 66 {
 67     if( sem_init(&blank, 0, BLANK_INIT) < 0)
 68     {
 69         printf("init product error....\n");
 70     }
 71     if( sem_init(&data, 0, DATA_INIT) < 0)
 72     {
 73         printf("init consumer error....\n");
 74     }
 75 }
 76 
 77 void destroy_allsem()
 78 {
 79     if( sem_destroy(&blank) < 0)
 80     {
 81         printf("destroy blank sem error...\n");
 82     }
 83     if( sem_destroy(&data) < 0)
 84     {
 85         printf("destroy data sem error...\n");
 86     }
 87 }
 88 
 89 
 90 
 91 int main()
 92 {
 93     init_allsem();
 94     run_product_consumer();
 95     destroy_allsem();
 96     return 0;
 97 }                                                                                                                                                              
                                                                                                                                                 97,1         底端

执行结果:

从图中可以看到有不同的生产者线程,不同的消费者线程。

总结:用于线程的信号量只实现同步功能,而互斥功能要用mutex实现;由于信号量的P,V操作的实现是原子的,所以加锁要加在P,V操作的后面;实现多消费者与多生产者时,消费者与消费者,生产者与生产者之间要实现互斥,而生产者与消费者之间只要实现同步。



附:

通过查看资料,发现一种比较简单的方法就是在代码中使用printf将当前线程的id打印出来。
而这也分成两种情况:
1. 如果是pthread,则使用,
#include <pthread.h>

pthread_t pthread_self(void);

2. 如果不是pthread,即是由内核创建的线程,则使用,
#include <sys/types.h>

pid_t gettid(void);

获取线程所在的进程的id,方法如下:
#include <sys/types.h>
#include <unistd.h>

pid_t getpid(void);
pid_t getppid(void);

所以,我们在代码中使用如下的语句打印:
printf("\ntid=%lu, pid=%lu\n", gettid(), getpid());
这样就能获取当前代码所在的线程和进程了。

时间: 2024-10-07 11:02:23

线程间同步之信号量实现环形buf的相关文章

Linux进程间通信与线程间同步详解(全面详细)

引用:http://community.csdn.net/Expert/TopicView3.asp?id=4374496linux下进程间通信的几种主要手段简介: 1. 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信:   2. 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身

Java并发:线程间同步-条件队列和同步工具类

转载请注明出处: jiq?钦's technical Blog - 季义钦 线程之间的同步,除了互斥(前面介绍的互斥锁)之外,还存在协作关系,下面我们就介绍一下java线程间常见的一些协作方式. 一.内置条件队列 正如每个Java对象都可以作为一个内置锁,每个对象也可以作为一个条件队列,称为内置条件队列,Object.wait().notify()/notifyAll()构成了内置条件队列的API. 需要注意的是,调用任何对象X的内置条件队列的API都必须要先获得该对象X的内置锁. 1.API介

rtt学习之线程间同步与通信

一 线程间的同步与互斥:信号量.互斥量.实践集 线程互斥是指对于临界区资源访问的排它性,如多个线程对共享内存资源的访问,生产消费型对产品的操作.临界区操作操作方法有: rt_hw_interrupt_disable() / rt_enter_critical()进入临界区, rt_hw_interrupt_enable() / rt_exit_critical()退出临界区 信号量的创建:rt_sem_t rt_sem_create(const char *name,rt_uint32_t va

conditon_variable(条件变量)用于线程间同步

conditon_variable(条件变量)用于线程间同步 condition_variable有5个函数,函数名及对应的功能如下: wait阻塞自己,等待唤醒 wait_for阻塞自己,等待唤醒,最多等待一段时间 wait_until阻塞自己,等待唤醒,最多等待到某个时间点 notify_one 唤醒一个等待在这个条件变量上的线程 notify_all 唤醒所有等待在这个条件变量上的线程 3个wait函数都要求输入一个已经上锁的unique_lock<mutex>变量,并且都有两个版本,一

linux线程间同步方式汇总

抽空做了下linux所有线程间同步方式的汇总(原生的),包含以下几个: 1, mutex 2, condition variable 3, reader-writer lock 4, spin lock 5, barrier mutex是最常用的线程间同步方式,主要目的是保护共享的资源可以被原子地访问. 个人感觉condition variable是除了mutex之外的第二常用的线程间同步方式,可以用来以同步的方式使用一个线程来通知另一个线程某个事件已经发生.可以理解为线程间的信号. reade

RT-Thread的线程间同步

禁止系统调度上一节< 多线程导致的临界区问题>中由于 test1 线程被 test2 线程打断,才导致了我们没有得到预期的结果,我们一般可通过关闭中断和调度器上锁这两种简单的途径来禁止系统调度,防止线程被打断,从而保证临界区不被破坏. 1. 关闭中断线程中关闭中断保护临界区的结构如下: #include <rtthread.h> #include <stm32f10x.h> #include "test.h" rt_uint32_t g_tmp;/*

android 线程间同步问题

我现在有2个android线程,一个是view 线程,一个是工作线程,我现在想让 工程线程暂停,直到view线程的 onDraw方法被调用后再继续运行 如何做?如何等待信号 处理方法 在java2个线程中传递对象,让一个线程去通知另一个线程的方式如下,在工作线程中调用 stick.wait(); When the view thread finishes its onDraw work, it calls this: 当view线程调用完onDraw的时候可以调用 stick.notify();

【C#】SynchronizationContext线程间同步

SynchronizationContext在通讯中充当传输者的角色,实现功能就是一个线程和另外一个线程的通讯. 需要注意的是,不是每个线程都附加SynchronizationContext这个对象,只有UI线程是一直拥有的.故获取SynchronizationContext也只能在UI线程上进行SynchronizationContext context = SynchronizationContext.Current; 那什么时候会用到呢? 在多线程操作时往往需要切回某个线程中去工作,等完成

linux线程间同步(1)读写锁

读写锁比mutex有更高的适用性,可以多个线程同时占用读模式的读写锁,但是只能一个线程占用写模式的读写锁. 1. 当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞: 2. 当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是以写模式对它进行枷锁的线程将阻塞: 3. 当读写锁在读模式锁状态时,如果有另外线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求长期阻塞: 这种锁适用对数据结