一、原理的简述
在生产者消费者模式当中,首先需要分清在这个模式当中有哪些角色?
各角色分别担任什么职责与它们之间的关系如何?
角色之间是在保证数据的准确性的情况下如何通信(同步数据)的?
假设现在有一个这样的情形:
有两个人共同访问一个容量有限的仓库,这2个人,其中一个是生产鞋子的,另一个是售卖鞋子,
他们共同使用一个仓库。在使用这个仓库之前,这2人之间需要建立一种规约,即:
1.生产鞋子的人首先需要向仓库管理员申请钥匙,在拿到钥匙的时候需要判断两种情况
在满仓的时候不能再让生产鞋子的人继续往这个仓库中存放鞋子,因为这个仓库的容量有限,
继续放会出现爆仓,那么这个时候生产鞋子的人应该怎么做呢?很明显,让它把仓库钥匙交出来,
然后出去等待。当仓库出现有位置空的时候,才继续存放鞋子。
2.卖鞋子的时候在使用这个仓库之前也需要向仓库管理员申请钥匙,当仓库有鞋子的时候,
把鞋子拿出来去卖,否则归还钥匙出仓等待。
3.这个仓库只有一把钥匙,即同时只能限制一个人进来。
二、将问题程序化:
.h文件:
//
// BaseRoom.h
// MultiThread
//
// Created by liuxiaobing on 2018/10/31.
// Copyright © 2018 liuxiaobing. All rights reserved.
//
#import
NS_ASSUME_NONNULL_BEGIN
/**
仓库
*/
@interface BaseRoom : NSObject
@property(strong,nonatomic) NSMutableArray* list;
@property(assign,nonatomic) BOOL bIsStop;
@property(strong,nonatomic) NSMutableArray* baseList; //仓库
@property(strong,nonatomic) dispatch_semaphore_t mutex; //访问仓库(临界区)的互斥访问信号量
@property(strong,nonatomic) dispatch_semaphore_t comsumer_sem; //生产者-是否生产对象的标记 消费者是否消费仓库对象的标记 使用信
@property(strong,nonatomic) dispatch_semaphore_t product_sem; //生产者-是否生产对象的标记 消费者是否消费仓库对象的标记 使用信
@property(nonatomic,assign) int count;
-(void) produce:(NSString*) e;
-(NSString*) comsumer;
@end
NS_ASSUME_NONNULL_END
.m文件 :
//
// BaseRoom.m
// MultiThread
//
// Created by liuxiaobing on 2018/10/31.
// Copyright © 2018 liuxiaobing. All rights reserved.
//
#import "BaseRoom.h"
@implementation BaseRoom
- (instancetype)init
{
self = [super init];
if (self) {
//新建一个仓库,这里暂不作容量设计
self.baseList = [[NSMutableArray alloc] init];
//初始化生产对象--消费者标记,初始为0表示什么都没有
self.product_sem = dispatch_semaphore_create(10);
self.comsumer_sem = dispatch_semaphore_create(0);
//初始化临界区互斥访问信号量,用信号量实现互斥,特殊初始值为1.
//控制同一时刻只有一个线程对象在访问仓库
self.mutex = dispatch_semaphore_create(1);
}
return self;
}
-(void)produce:(NSString *)e{
long baseCount = dispatch_semaphore_wait(self.mutex, 5 * NSEC_PER_SEC); //先获取访问仓库的信号量
if(baseCount != 0){
NSLog(@"39----------仓库有人正在使用,生产者处于等待");
}
long maxSpaceCount = dispatch_semaphore_wait(self.product_sem, 5 * NSEC_PER_SEC); //再判断 仓库是否还有可放物品的空间
if(maxSpaceCount != 0){
NSLog(@"43----------仓库10个空间已经使用完,生产者处于等待:仓库容量:%lu",[self.baseList count]);
}else{
[self.baseList addObject:e];
NSLog(@"40---------生产鞋子%@:w仓库目前有:%lu",e,[self.baseList count]);
dispatch_semaphore_signal(self.mutex); //生产完了释放临界区的访问锁
dispatch_semaphore_signal(self.comsumer_sem); //将仓库中的皮鞋数量加1
}
}
-(NSString*) comsumer{
NSString* e = nil;
long baseCount = dispatch_semaphore_wait(self.mutex, 5 * NSEC_PER_SEC); //先获取访问仓库的信号量
if(baseCount != 0){
NSLog(@"55----------仓库有人正在使用,消费者处于等待");
}
long avableCount = dispatch_semaphore_wait(self.comsumer_sem, 5 * NSEC_PER_SEC); //再判断 仓库是否还有可取,如果有物品,则取一个出来,否则t等待
if(avableCount != 0){
NSLog(@"59----------空仓,消费者处于等待");
}else{
e = [self.baseList objectAtIndex:[self.baseList count] -1];
[self.baseList removeLastObject];
NSLog(@"50---------卖鞋子:%@ 仓库还有%lu:",e,[self.baseList count]);
dispatch_semaphore_signal(self.mutex); //生产完了释放临界区的访问锁
dispatch_semaphore_signal(self.product_sem); //将仓库中的可放置的数量 +1
}
return e;
}
@end
测试页面:
.h文件
//
// ProComsumerModel.h
// MultiThread
//
// Created by liuxiaobing on 2018/10/31.
// Copyright © 2018 liuxiaobing. All rights reserved.
//
#import
#import "BaseRoom.h"
NS_ASSUME_NONNULL_BEGIN
/**
生产者消费者模式
*/
@interface ProComsumerModel : UIViewController
@property(nonatomic,strong) BaseRoom* baseroom;
//生产者线程跑的队列,这个队列可以控制生产者的执行是并行还是串行
@property(strong,nonatomic)dispatch_queue_t producerQueue;
//消费者线程跑的队列,这个队列可以控制消费者的执行是并行还是串行
@property(strong,nonatomic) dispatch_queue_t consumerQueue;
-(void) initObj;
@end
NS_ASSUME_NONNULL_END
.m
//
// ProComsumerModel.m
// MultiThread
//
// Created by liuxiaobing on 2018/10/31.
// Copyright © 2018 liuxiaobing. All rights reserved.
//
#import "ProComsumerModel.h"
#import "ProComsumerModel.h"
@interface ProComsumerModel ()
@end
@implementation ProComsumerModel
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self initObj];
[self initView];
}
-(void) initView{
UIButton* button2 = [UIButton buttonWithType:UIButtonTypeCustom];
[button2 setTitle:@"生产" forState:UIControlStateNormal];
button2.frame = CGRectMake(20, 80, 200, 40);
button2.backgroundColor = [UIColor brownColor];
[button2 addTarget:self action:@selector(producer:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button2];
UIButton* button3 = [UIButton buttonWithType:UIButtonTypeCustom];
[button3 setTitle:@"消费" forState:UIControlStateNormal];
button3.frame = CGRectMake(20, 180, 200, 40);
button3.backgroundColor = [UIColor brownColor];
[button3 addTarget:self action:@selector(comsumer:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button3];
}
-(void)producer:(UIButton*) button{
[self producter];
}
-(void)comsumer:(UIButton*) button{
[self comsumer];
}
-(void) initObj{
self.baseroom = [[BaseRoom alloc] init];
//分别创建N个生产者和M消费者各自的运行并发队列
//均使用并发队列,即生产者之间可以并发执行,消费者之间也可以并发执行
self.producerQueue = dispatch_queue_create("producer", DISPATCH_QUEUE_CONCURRENT);
self.consumerQueue = dispatch_queue_create("consumer", DISPATCH_QUEUE_CONCURRENT);
}
-(void) producter{
// //创建10个生产者持续生产皮鞋
// for (int i = 0; i < 1; i++) {
//
// }
dispatch_async(self.producerQueue, ^{
while(1){
NSString* t = [NSString stringWithFormat:@"nike"];
[self.baseroom produce:t];
sleep(1);
}
});
}
-(void)comsumer{
dispatch_async(self.consumerQueue, ^{
while(1){
[self.baseroom comsumer];
sleep(2);
}
});
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
测试效果:
工程源码