IOS中使用GCD与信号量机制实现读者写者(读者优先)

一、读者写者问题是操作系统中非常经典的线程同步问题,像jdk中有读写锁用来处理这类问题。在读者写者模式中又有几种不同的同步模式,如:

  • 读者优先 式的读者写者类型
  • 写者优先 式的读者写者类型
  • 公平竞争 式的读者写者类型

但不管是哪种类型,在处理 这个问题时都遵循下列几个互斥条件,否则会出现“假死”即都处于等待,或出现脏数据问题。

  • 允许读者–读者之间同时执行读操作临界资源
  • 不允许写者–写者之间同时操作操作临界资源
  • 不允许读者–写者同时操作操作临界资源

读者优先解决方法(其他类型看官们自己类推):
读者访问临界资源的逻辑:

  • 无其他读者、写者,该读者可以读
  • 若已有写者等待,但有其他读者正在读,则该读者也可以读
  • 若有写者正在写,则该读者必须等如果写者执行
  • 使用一个计数器记录当前读者访问临界资源的数量,在访问完临界资源的时候,对这个计数器进行减一操作,当计数器值为0时,释放读者写者之间的锁。

写者访问临界资源的逻辑:

  • 无其他读者、写者,该写者可以写
  • 若有读者正在读,该写者等待
  • 若有其他读者正在写,该写者等待

测试页面的.h文件

//
//  ReadWriteVC.h
//  MultiThread
//
//  Created by liuxiaobing on 2018/11/1.
//  Copyright © 2018 liuxiaobing. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "ReaderWrite.h"

NS_ASSUME_NONNULL_BEGIN


/**
 6、读者写者问题:
 (1)满足条件:①写写互斥;②读写互斥;③读读同时
 (2)单纯使用信号量不能解决读者与写者问题,必须引入计数器readcount对读进程计数;
     rmutex是用于对计数器readcount操作的互斥信号量;wmutex表示是否允许写的信号量;
 */
@interface ReadWriteVC : UIViewController

//写者线程跑的队列,这个队列可以控制读者的执行是并行还是串行
@property(strong,nonatomic) dispatch_queue_t readQueue;


//读者线程跑的队列,这个队列可以控制写者的执行是并行还是串行
@property(strong,nonatomic) dispatch_queue_t writeQueue;


@property(strong,nonatomic) ReaderWrite* readWriteMgr;

@end

NS_ASSUME_NONNULL_END

.m

//
//  ReadWriteVC.m
//  MultiThread
//
//  Created by liuxiaobing on 2018/11/1.
//  Copyright © 2018 liuxiaobing. All rights reserved.
//

#import "ReadWriteVC.h"

@interface ReadWriteVC ()

@end

@implementation ReadWriteVC

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    self.readWriteMgr = [[ReaderWrite alloc] init];
    [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(read:) 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(write:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button3];
    
    self.readQueue = dispatch_queue_create("producer", DISPATCH_QUEUE_CONCURRENT);  //读者可以使用 并发队列,不限定读者的顺序
    self.writeQueue = dispatch_queue_create("consumer", DISPATCH_QUEUE_SERIAL);
}


-(void) read:(UIButton*) button{
    
    for(int i = 0; i < 5; i ++){
        dispatch_async(self.readQueue,  ^{
            while(1){
                [self.readWriteMgr readData];
                sleep(1);
            }

        });
    }
    
    
}

-(void) write:(UIButton*) button{
    
    for(int i = 0; i < 5; i ++){
        dispatch_async(self.writeQueue,  ^{
            while(1){
                [self.readWriteMgr writeData];

                sleep(1);
            }

        });
    }
    
}


/*
#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

读者写者核心类.h文件:

//
//  ReaderWrite.h
//  MultiThread
//
//  Created by liuxiaobing on 2018/11/1.
//  Copyright © 2018 liuxiaobing. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN


/**
 (1)满足条件:①写写互斥;②读写互斥;③读读同时
 
 (2)单纯使用信号量不能解决读者与写者问题,必须引入计数器readcount对读进程计数;
    rmutex是用于对计数器readcount操作的互斥信号量;
    wmutex表示是否允许写的信号量;
 */
@interface ReaderWrite : NSObject

@property(assign,nonatomic) int board;           //临界资源
@property(strong,nonatomic) dispatch_semaphore_t rmutex;         //读者计数器 互斥信号量
@property(strong,nonatomic) dispatch_semaphore_t wmutex;         //写者-读者, 写者-写者
@property(nonatomic,assign) int readcount;

-(void) readData;

-(void) writeData;

@end

NS_ASSUME_NONNULL_END

.m文件

//
//  ReaderWrite.m
//  MultiThread
//
//  Created by liuxiaobing on 2018/11/1.
//  Copyright © 2018 liuxiaobing. All rights reserved.
//

#import "ReaderWrite.h"

@implementation ReaderWrite

- (instancetype)init
{
    self = [super init];
    if (self) {
        
        //初始化生产对象--消费者标记,初始为0表示什么都没有
        self.rmutex = dispatch_semaphore_create(1);
        //初始化临界区互斥访问信号量,用信号量实现互斥,特殊初始值为1.
        //控制同一时刻只有一个线程对象在访问临界资源
        self.wmutex = dispatch_semaphore_create(1);
        self.readcount = 0; //当前正在读数据的读者数量
    }
    return self;
}


-(void) readData{
    
    
    long baseCount = dispatch_semaphore_wait(self.rmutex,  5 * NSEC_PER_SEC);       
    if(baseCount != 0){
        NSLog(@"36----------临界区正在被写者使用,读者处于等待");
    }else{
        if(self.readcount == 0){
            long tmp = dispatch_semaphore_wait(self.wmutex,  5 * NSEC_PER_SEC);
            if(tmp != 0){
                NSLog(@"36----------我是第一个读者,我想在临界区读取数据,但是当前有写者,读者处于等待");
            }else{

            }
        }

        self.readcount ++;
        dispatch_semaphore_signal(self.rmutex);  //释放读者计数器的锁
        NSLog(@"50----------读者开始读数据:%d",self.board);
        
        dispatch_semaphore_wait(self.rmutex,  5 * NSEC_PER_SEC);    //读完数据之后还需要再申请使用一次读者之间的x互斥锁,
        self.readcount --;          //将读者计数器 减一操作
        if(self.readcount == 0){    //如果是最后一个t读者操作,需要通知 写者可以 进行写操作了
            dispatch_semaphore_signal(self.wmutex);
        }
        dispatch_semaphore_signal(self.rmutex); //最后再释放 读者之间的锁
    }
    
  
}

-(void) writeData{
    
    long count = dispatch_semaphore_wait(self.wmutex,  5 * NSEC_PER_SEC);
    if(count != 0){
        NSLog(@"50----------当前有人在使用临界区在读数据,写者等待....");
        
    }else{
//        self.board = (arc4random() % 100) + 1;
//        NSLog(@"71----------写者写数据:%d",self.board);
       
    }
    self.board = (arc4random() % 100) + 1;
    NSLog(@"71----------写者写数据:%d",self.board);
     dispatch_semaphore_signal(self.wmutex);
    
}

@end

测试结果 :
在这里插入图片描述

参考文档
https://blog.csdn.net/cz_hyf/article/details/4443551

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值