写高质量OC代码52建议总结:41.多用派发列队,少用同步锁

如果有多个线程要执行同一份代码,有时会出问题。对数据的读写时机不可控。通常要使用锁机制来实现同步。有两种办法。
 -(void)synchronizedMethod {
    @synchronized(self) {
        //  safe
    }
 }
 这种方式会根据给定的对象,自动创建一个锁,等块中的代码执行完成,锁就释放。例子中的对象都是self,这样可以保证每个self的实例对象都可以不受干扰的运行synchronizedMethod。但是,滥用@synchronized(self)会降低代码效率。因为所有的调用都会排队进行。
 另一个方法是使用NSLock对象:
 _lock = [[NSLock alloc] init];
 -(void)synchronizedMethod {
    [_lock lock];
    //  Safe
    [_lock unlock];
 }
 也可以用NSRecursiveLock这种“递归锁”,线程可以多次持有该锁,而不会出现死锁。这两种方式在极端情况下,同步块会导致死锁,其效率也不是很高。如果直接使用锁对象,遇到死锁就会很麻烦。
 替代方案就是使用GCD,比如,属性就是经常需要同步的地方,这种属性要做成“原子的”,用atomic修饰。如果开发者想要自己实现的话:
 -(NSSTring *)someString {
    @synchronized(self) {
        return _someString;
    }
 }
 -(void)setSomeString:(NSString *)someString {
    @synchronized(self) {
        _someString = someString;
    }
 }
 这么做虽然可以提供某种程度的线程安全,但是无法保证访问该对象绝对是线程安全的。访问属性的操作确实是原子的。同一个线程上多次调用获取方法,结果未必相同,两次访问中间的写入操作会改变其值。
 使用串行同步队列可保证数据同步。
 
_syncQueue = dispatch_queue_creat("com.effectiveobjectivec.syncQueue", NULL);
 -(NSString *)someString {
    __block NSString *localSomeString;
     dispatch_sync(_syncQueue, ^{
        localSomeString = _someString;
     });
     return localSomeString;
 }
 -(void)setSomeString:(NSString *)someString {
    dispatch_sync(_syncQueue, ^{
         _someString = someString;
     });
 }
 把设置操作和获取操作都安排在序列化列队中执行。优化一下,设置方法并不一定必须同步。
 -(void)setSomeString:(NSString *)someString {
    dispatch_async(_syncQueue, ^{
        _someString = someString;
     });
 }
 这种写法可以提升设置方法的执行速度。但是会使程序性能下降。因为执行异步派发需要拷贝块,如果拷贝所花的时间超过执行所花的时间,这种做法会比原来慢。
 可以将程序改为并发执行。
 
_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 -(NSString *)someString {
     __block NSString *localSomeString;
     dispatch_sync(_syncQueue, ^{
         localSomeString = _someString;
     });
     return localSomeString;
 }
 -(void)setSomeString:(NSString *)someString {
    dispatch_async(_syncQueue, ^{
        _someString = someString;
     });
 }
 这样暂时还不是同步,所有的操作都会在同一个队列里,由于是并发队列,所有操作可以随意执行。可以用栅栏解决。
 void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
 void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
 并发列队如果发现接下来处理的是个栅栏,就会优先将其他块处理完,然后在单独执行栅栏。在本例中可以对设置方法实现栅栏,属性的读取操作依然可以并发执行,写入操作单独执行。
 
-(void)setSomeString:(NSString *)someString {
     dispatch_barrier_async(_syncQueue, ^{
         _someString = someString;
     });
 }
 总结:
 1.派发队列可以表述同步语义,比@synchronized和NSLock对象要简单。
 2.将同步和异步派发结合起来,可以实现与普通加锁机制一样的同步行为,而不会阻塞执行异步派发的线程。

 3.使用同步队列及栅栏块,可以令同步行为更加高效。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值