iOS开发技能iOS Developer

多用派发队列 少用同步锁

2018-01-28  本文已影响8人  AFWater

今天重温了 《Effective Objective-C 2.0》,里面有一个章节 多用派发队列,少用同步锁 以前被忽视了,写出来备份下,以下内容为该章节内容抄录和总结。

在Objective-C中,如果有多个线程要执行同一份代码,那么有时可能会出问题,这种情况一般有三种办法:

相对来说,GCD更加简单而高效。

举个例子,要保证对某一个属性的线程安全,此时使用GCD有两种做法

使用串行队列

_syncqueue = dispatch_queue_create("com.test.gcd", NULL);

- (NSString *)something{
    __block NSString *localSomething = nil;
    dispatch_sync(_syncqueue, ^{
        localSomething = _something;
    });
    return localSomething;
}

- (void)setSomething:(NSString *)something{
    dispatch_sync(_syncqueue, ^{
        _something = something;
    });
}

此模式把设置操作和读取操作都放到串行队列里执行,所有对属性的访问就都同步了。

使用并发队列

其实,对于属性来说,多个get方法是可以并发执行的,而get方法和set方法之间不能并发执行。而且set方法可以是异步的,因为set方法不需要返回值。

按照这个思路,把队列改成并发队列,,并使用 dispatch_barrier_async 函数,使 get 方法和 set方法 不并发执行。

_syncqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

- (NSString *)something{
    __block NSString *localSomething = nil;
    dispatch_sync(_syncqueue, ^{
        localSomething = _something;
    });
    return localSomething;
}

- (void)setSomething:(NSString *)something{
    dispatch_barrier_async(_syncqueue, ^{
        _something = something;
    });
}

由于set方法中使用了栅栏块,一旦有写入操作时,就会等所有读取的操作全部执行完才会执行写入操作。

同时注意set方法 dispatch_barrier_async为异步,因为set方法不需要同步,从调用者的角度来讲使用异步会加快执行速度。但是这么做有一个坏处:执行异步派发时,需要拷贝块,如果拷贝快的时间明显超过执行快的所花的时间,则这种做法比使用同步会更慢,但是如果块要执行的为更繁重的任务,那么可以考虑使用异步。对于本文中的例子,只是简单的set,则不需要使用异步派发。

不过这个示例还有点问题,因为使用了全局并发队列,可能会阻塞系统的队列,所以最好使用自定义并发队列。

_syncqueue = dispatch_queue_create("com.test.concurrent", DISPATCH_QUEUE_CONCURRENT);

- (NSString *)something{
    __block NSString *localSomething = nil;
    dispatch_sync(_syncqueue, ^{
        localSomething = _something;
    });
    return localSomething;
}

- (void)setSomething:(NSString *)something{
    dispatch_barrier_async(_syncqueue, ^{
        _something = something;
    });
}
上一篇下一篇

猜你喜欢

热点阅读