IOS多线程使用GCD与信号量实现生产者与消费者模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/d06110902002/article/details/75196862

一、原理的简述  

在生产者消费者模式当中,首先需要分清在这个模式当中有哪些角色?

各角色分别担任什么职责与它们之间的关系如何?

角色之间是在保证数据的准确性的情况下如何通信(同步数据)的?

假设现在有一个这样的情形:

有两个人共同访问一个容量有限的仓库,这2个人,其中一个是生产鞋子的,另一个是售卖鞋子,

他们共同使用一个仓库。在使用这个仓库之前,这2人之间需要建立一种规约,即:

1.生产鞋子的人首先需要向仓库管理员申请钥匙,在拿到钥匙的时候需要判断两种情况

在满仓的时候不能再让生产鞋子的人继续往这个仓库中存放鞋子,因为这个仓库的容量有限,

继续放会出现爆仓,那么这个时候生产鞋子的人应该怎么做呢?很明显,让它把仓库钥匙交出来,

然后出去等待。当仓库出现有位置空的时候,才继续存放鞋子。

2.卖鞋子的时候在使用这个仓库之前也需要向仓库管理员申请钥匙,当仓库有鞋子的时候,

把鞋子拿出来去卖,否则归还钥匙出仓等待。

3.这个仓库只有一把钥匙,即同时只能限制一个人进来。

二、将问题程序化:

.h文件:

//
//  BaseRoom.h
//  MultiThread
//
//  Created by liuxiaobing on 2018/10/31.
//  Copyright © 2018 liuxiaobing. All rights reserved.
//

#import <Foundation/Foundation.h>

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 <UIKit/UIKit.h>
#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

测试效果:

工程源码

猜你喜欢

转载自blog.csdn.net/d06110902002/article/details/75196862