加入收藏 | 设为首页 | 会员中心 | 我要投稿 威海站长网 (https://www.0631zz.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长资讯 > 评论 > 正文

iOS多线程开发:几个容易被忽略的细节

发布时间:2019-06-07 02:30:16 所属栏目:评论 来源:Eternal_Love
导读:一般情况下,iOS开发者只要会使用GCD、@synchronized、NSLock等几个简单的API,就可以应对大部分多线程开发了,不过这样是否真正做到了多线程安全,又是否真正充分利用了多线程的效率优势呢?看看以下几个容易被忽略的细节。 读者写者问题(Readers-writers

(2)串行队列上没必要使用GCD barrier,应该使用dispatch_queue_create建立的并发队列;dispatch_get_global_queue由于是全局共享队列,使用barrier达不到隔离当前任务的效果,会自动降级为dispatch_sync / dispatch_async。[5]

锁的粒度(Granularity)

首先看两段代码:

代码段1

  1. @property (atomic, copy) NSString *atomicStr; 
  2.  
  3. //thread A 
  4. atomicSr = @"am on thread A"; 
  5. NSLog(@"%@", atomicStr); 
  6.  
  7. //thread B 
  8. atomicSr = @"am on thread B"; 
  9. NSLog(@"%@", atomicStr); 

代码段2

  1. - (void)synchronizedAMethod { 
  2.     @synchronized (self) { 
  3.         ... 
  4.     } 
  5.  
  6. - (void)synchronizedBMethod { 
  7.     @synchronized (self) { 
  8.         ... 
  9.     } 
  10.  
  11. - (void)synchronizedCMethod { 
  12.     @synchronized (self) { 
  13.         ... 
  14.     } 

粒度过小

执行代码段1,在线程A上打印出来的字符串却可能是“am on thread B”,原因是虽然atomicStr是原子操作,但是取出atomicStr之后,在执行NSLog之前,atomicStr仍然可能会被线程B修改。因此atomic声明的属性,只能保证属性的get和set是完整的,但是却不能保证get和set完之后的关于该属性的操作是多线程安全的,这就是aomic声明的属性不一定能保证多线程安全的原因。

同样的,不仅仅是atomic声明的属性,在开发中自己加的锁如果粒度太小,也不能保证线程安全,代码段1其实和下面代码效果一致:

  1. @property (nonatomic, strong) NSLock *lock; 
  2. @property (nonatomic, copy) NSString *atomicStr; 
  3.  
  4. //thread A 
  5. [_lock lock]; 
  6. atomicSr = @"am on thread A"; 
  7. [_lock unlock]; 
  8. NSLog(@"%@", atomicStr); 
  9.  
  10. //thread B 
  11. [_lock lock]; 
  12. atomicSr = @"am on thread B"; 
  13. [_lock unlock]; 
  14. NSLog(@"%@", atomicStr); 

如果想让程序按照我们的初衷,设置完atomicStr后打印出来的就是设置的值,就需要加大锁的范围,将NSLog也包括在临界区内:

  1. //thread A 
  2. [_lock lock]; 
  3. atomicSr = @"am on thread A"; 
  4. NSLog(@"%@", atomicStr); 
  5. [_lock unlock]; 
  6.  
  7. //thread B 
  8. [_lock lock]; 
  9. atomicSr = @"am on thread B"; 
  10. NSLog(@"%@", atomicStr); 
  11. [_lock unlock]; 

示例代码很简单,很容易看出问题所在,但是在实际开发中遇到更复杂些的代码块时,一不小心就可能踏入坑里。因此在设计多线程代码时,要特别注意代码之间的逻辑关系,若后续代码依赖于加锁部分的代码,那这些后续代码也应该一并加入锁中。

粒度过大

@synchronized关键字会自动根据传入对象创建一个与之关联的锁,在代码块开始时自动加锁,并在代码块结束后自动解锁,语法简单明了,很方便使用,但是这也导致部分开发者过渡依赖于@synchronized关键字,滥用@synchronized(self)。如上述代码段2中的写法,在一整个类文件里,所有加锁的地方用的都是@synchronized(self),这就可能会导致不相关的线程执行时都要互相等待,原本可以并发执行的任务不得不串行执行。另外使用@synchronized(self)还可能导致死锁:

  1. //class A 
  2.  
  3. @synchronized (self) { 
  4.     [_sharedLock lock]; 
  5.     NSLog(@"code in class A"); 
  6.     [_sharedLock unlock]; 
  7.  
  8. //class B 
  9. [_sharedLock lock]; 
  10. @synchronized (objectA) { 
  11.     NSLog(@"code in class B"); 
  12. [_sharedLock unlock]; 

(编辑:威海站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读