利用操作队列和信号量来实现操作同步

     最近在开发iOS过程中遇到一个问题:某一些操作需要在一个初始化操作后才允许执行。但是这些操作的执行时刻有可能比初始化操作来得要快。那么,如果不等待初始化操作后再执行的话,这些操作就等于是丢失了。

     针对这个问题,我想到了两种解决方案:第一就是执行这些操作之前先判断是否已经初始化,如果尚未初始化则使用一个数组队列把操作参数及调用的方法存储起来,等待初始化完成后再检测数组队列中的保存的操作进行调用并清空队列。但这种方式有个问题就是操作中传递的参数以及调用方法引用都需要自己来维护,这无疑是给自己带来了一定的工作量以及风险,稍有不慎就有可能会导致内存泄露。

     因此第二中解决方法就是利用串行队列结合信号量的方式来控制操作的执行。此方案的思路是,先创建一条串行队列,此队列用于执行所有的操作。但是最先入队的是一个等待信号的操作。而这个信号的初始值是0,直到初始化操作完成后才会发送一个信号来通知此操作。因此,在尚未初始化完成的时候此队列是一直处于阻塞状态的。所以到有操作进入队列时都会立刻执行,而是需要等到初始化信号过来后才开始执行。

     为了验证这一想法,我新建了一个应用工程,在ViewController中定义了操作队列_quque和信号量_sema,如下:

@interface ViewController : UIViewController
{
@private
    dispatch_queue_t _queue;
    dispatch_semaphore_t _sema;
}

@end
初始化时创建操作队列
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])
    {
        _queue = dispatch_queue_create("cn.vimfung.demo", DISPATCH_QUEUE_SERIAL);
    }
    
    return self;
}

     在ViewController中定义了三个按钮,分别为DoSomething、Signal、Wait。其中DoSomething为执行的操作。Signal为通知阻塞队列可以执行操作了。Wait为阻塞当前队列。

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [btn setTitle:@"DoSomething" forState:UIControlStateNormal];
    [btn sizeToFit];
    [btn addTarget:self action:@selector(doSomethingHandler:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
    
    UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [btn1 setTitle:@"Signal" forState:UIControlStateNormal];
    [btn1 sizeToFit];
    [btn1 addTarget:self action:@selector(signalHanlder:) forControlEvents:UIControlEventTouchUpInside];
    btn1.frame = CGRectMake(0.0, 50.0, btn1.frame.size.width, btn1.frame.size.height);
    [self.view addSubview:btn1];
    
    UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [btn2 setTitle:@"Wait" forState:UIControlStateNormal];
    [btn2 sizeToFit];
    [btn2 addTarget:self action:@selector(waitHanlder:) forControlEvents:UIControlEventTouchUpInside];
    btn2.frame = CGRectMake(0.0, 100.0, btn2.frame.size.width, btn2.frame.size.height);
    [self.view addSubview:btn2];
}

- (void)doSomethingHandler:(id)sender
{
    dispatch_async(_queue, ^{
        NSLog(@"do something");
    });
}

- (void)signalHanlder:(id)sender
{
    dispatch_semaphore_signal(_sema);
}

- (void)waitHanlder:(id)sender
{
    if (_sema)
    {
        dispatch_release(_sema);
    }
    _sema = dispatch_semaphore_create(0);
    dispatch_async(_queue, ^{
        dispatch_semaphore_wait(_sema, DISPATCH_TIME_FOREVER);
    });
}

      运行后,先点击Wait让队列阻塞、然后这时无论怎么点击DoSomething都是不会有log信息显示,直到点击Signal后,之前点击的DoSomething将会一一打印出来信息。

      可见这种解决方案是可行的,并且可以更加容易操作。

你可能感兴趣的:(ios,队列,信号量,操作同步)