生产者-消费者模型
2017-04-07 17:47
183 查看
在写生产者—消费者模型时,先谈及一个概念——信号量。
sem函数:
(1)sem_init函数是Posix信号量操作中的函数。
sem_init() 初始化一个定位在 sem 的匿名信号量。
value 参数指定信号量的初始值。 pshared 参数指明信号量是由进程内线程共享,还是由进程之间共享。如果 pshared 的值为 0,那么信号量将被进程内的线程共享,并且应该放置在这个进程的所有线程都可见的地址上(如全局变量,或者堆上动态分配的变量)。
函数原型:int sem_init(sem_t *sem, int pshared, unsigned int value);
如果 pshared 是非零值,那么信号量将在进程之间共享,并且应该定位共享内存区域(见 shm_open(3)、mmap(2) 和 shmget(2))。因为通过 fork(2) 创建的孩子继承其父亲的内存映射,因此它也可以见到这个信号量。所有可以访问共享内存区域的进程都可以用 sem_post(3)、sem_wait(3) 等等操作信号量。初始化一个已经初始的信号量其结果未定义。
(2)sem_wait函数也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。//P操作
也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,将信号量的值将减到1。如果对一个值为0的信号量调用sem_wait(),这个函数就会原地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加 一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。sem_trywait(sem_t *sem)是函数sem_wait的非阻塞版,它直接将信号量sem减1,同时返回错误代码。
函数原型 :int sem_wait(sem_t * sem);
sem_wait() 减小(锁定)由sem指定的信号量的值.如果信号量的值比0大,那么进行减一的操作,函数立即返回.
如果信号量当前为0值,那么调用就会一直阻塞直到或者是信号量变得可以进行减一的操作
(例如,信号量的值比0大),或者是信号处理程序中断调用
sem_trywait() 和 sem_wait()是一样的,除了如果不能够对信号量立即进行减一,
那么sem_trywait()就会返回一个错误(错误号是AGAIN)而不是锁定. //非阻塞式等待
sem_timedwait() 和 sem_wait()是一样的,除了如果减一操作不能立即执行的话,
(3)sem_destroy() 销毁由sem指向的匿名信号量。
函数原型:int sem_destroy(sem_t *sem);
sem_destroy() 销毁由sem指向的匿名信号量。
只有通过sem_init(3) 初始化的信号量才应该使用sem_destroy() 销毁。
销毁一个有其它线程或进程当前阻塞(在sem_wait(3))的信号量将导致未定义行为。
使用一个已经销毁的信号量将导致未定义结果,除非这个信号量已经使用sem_init(3) 重新初始化了。
返回值:sem_destroy() 成功时返回 0;错误时,返回 -1,并把errno设置为合适的值。
(4)sem_post是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同 时对同一个文件进行读、加和写操作的两个程序就有可能会引起冲突。//V操作
函数原型:int sem_post(sem_t *sem);
信号量的值永远会正确地加一个“2”--因为有两个线程试图改变它。 当有线程阻塞在这个信号量上时,调用这个函数会使其中一个线程不在阻塞,选择机制是有线程的调度策略决定的。
返回值:sem_post() 成功时返回 0;错误时,信号量的值没有更改,-1 被返回,并设置 errno 来指明错误。
生产者-消费者模型(环形队列实现)
(1)用计数器实现判空,判满
(2)在队列为空时,保证生产者先进行,队列为满时,消费者先进行
代码如下:
源代码如下:
运行结果如下:
sem函数:
(1)sem_init函数是Posix信号量操作中的函数。
sem_init() 初始化一个定位在 sem 的匿名信号量。
value 参数指定信号量的初始值。 pshared 参数指明信号量是由进程内线程共享,还是由进程之间共享。如果 pshared 的值为 0,那么信号量将被进程内的线程共享,并且应该放置在这个进程的所有线程都可见的地址上(如全局变量,或者堆上动态分配的变量)。
函数原型:int sem_init(sem_t *sem, int pshared, unsigned int value);
如果 pshared 是非零值,那么信号量将在进程之间共享,并且应该定位共享内存区域(见 shm_open(3)、mmap(2) 和 shmget(2))。因为通过 fork(2) 创建的孩子继承其父亲的内存映射,因此它也可以见到这个信号量。所有可以访问共享内存区域的进程都可以用 sem_post(3)、sem_wait(3) 等等操作信号量。初始化一个已经初始的信号量其结果未定义。
(2)sem_wait函数也是一个原子操作,它的作用是从信号量的值减去一个“1”,但它永远会先等待该信号量为一个非零值才开始做减法。//P操作
也就是说,如果你对一个值为2的信号量调用sem_wait(),线程将会继续执行,将信号量的值将减到1。如果对一个值为0的信号量调用sem_wait(),这个函数就会原地等待直到有其它线程增加了这个值使它不再是0为止。如果有两个线程都在sem_wait()中等待同一个信号量变成非零值,那么当它被第三个线程增加 一个“1”时,等待线程中只有一个能够对信号量做减法并继续执行,另一个还将处于等待状态。sem_trywait(sem_t *sem)是函数sem_wait的非阻塞版,它直接将信号量sem减1,同时返回错误代码。
函数原型 :int sem_wait(sem_t * sem);
sem_wait() 减小(锁定)由sem指定的信号量的值.如果信号量的值比0大,那么进行减一的操作,函数立即返回.
如果信号量当前为0值,那么调用就会一直阻塞直到或者是信号量变得可以进行减一的操作
(例如,信号量的值比0大),或者是信号处理程序中断调用
sem_trywait() 和 sem_wait()是一样的,除了如果不能够对信号量立即进行减一,
那么sem_trywait()就会返回一个错误(错误号是AGAIN)而不是锁定. //非阻塞式等待
sem_timedwait() 和 sem_wait()是一样的,除了如果减一操作不能立即执行的话,
(3)sem_destroy() 销毁由sem指向的匿名信号量。
函数原型:int sem_destroy(sem_t *sem);
sem_destroy() 销毁由sem指向的匿名信号量。
只有通过sem_init(3) 初始化的信号量才应该使用sem_destroy() 销毁。
销毁一个有其它线程或进程当前阻塞(在sem_wait(3))的信号量将导致未定义行为。
使用一个已经销毁的信号量将导致未定义结果,除非这个信号量已经使用sem_init(3) 重新初始化了。
返回值:sem_destroy() 成功时返回 0;错误时,返回 -1,并把errno设置为合适的值。
(4)sem_post是给信号量的值加上一个“1”,它是一个“原子操作”---即同时对同一个信号量做加“1”操作的两个线程是不会冲突的;而同 时对同一个文件进行读、加和写操作的两个程序就有可能会引起冲突。//V操作
函数原型:int sem_post(sem_t *sem);
信号量的值永远会正确地加一个“2”--因为有两个线程试图改变它。 当有线程阻塞在这个信号量上时,调用这个函数会使其中一个线程不在阻塞,选择机制是有线程的调度策略决定的。
返回值:sem_post() 成功时返回 0;错误时,信号量的值没有更改,-1 被返回,并设置 errno 来指明错误。
生产者-消费者模型(环形队列实现)
(1)用计数器实现判空,判满
(2)在队列为空时,保证生产者先进行,队列为满时,消费者先进行
代码如下:
源代码如下:
#include<stdio.h> #include<unistd.h> #include<pthread.h> #include<semaphore.h> #include<stdlib.h> #define SIZE 64 //环形队列大小64 sem_t blanks; //格子 sem_t datas; //数据 int ring[SIZE]; void* producer(void* arg) { static int i = 0; while(1) { sem_wait(&blanks); int num = rand()%1000; ring[i++] = num; printf("producer:%d, tid:%lu\n", num, pthread_self()); i %= SIZE; sem_post(&datas); } sleep(1); } void* consumer(void* arg) { static int i=0; while(1) { sem_wait(&datas); int num = ring[i++]; printf("consumer: %d, tid:%lu\n:", pthread_self()); i %= SIZE; sem_post(&blanks); sleep(1); } } int main() { pthread_t id1, id2; sem_init(&blanks, 0, SIZE); sem_init(&datas, 0, 0); pthread_create(&id1, NULL, producer, NULL); pthread_create(&id2, NULL, consumer, NULL); sem_destroy(&blanks); sem_destroy(&datas); pthread_join(id1, NULL); pthread_join(id2, NULL); return 0; }
运行结果如下:
相关文章推荐
- 并发协作-生产者消费者模型
- 并发协作-生产者消费者模型
- 并发协作-生产者消费者模型
- Java线程(学习整理)--4---一个简单的生产者、消费者模型
- wait,notify,非阻塞队列实现生产者,消费者模型
- 生产者与消费者模型(重要)
- 生产者消费者模型
- 生产者与消费者模型
- python3用消费者和生产者模型,实现视频流读取播放
- 自己用Java写的简单生产者与消费者模型
- 多线程模拟实现生产者/消费者模型
- 基于内置锁的生产者消费者模型
- 生产者消费者模型的四种java编程例子(转载)
- python生产者消费者模型
- [C++]_[初级]_[pthread多线程之生产者消费者模型]
- Java并发编程中的生产者与消费者模型简述
- 简单实现带有数据缓冲池的生产者消费者模型
- Kafka消费者生产者编程模型(二)
- 生产者、消费者模型
- Linux中的生产者消费者模型