什么时候需要用到同步
在同一块内存,可能会被多个线程并发访问的情况下,一般考虑数据同步
同步机制分为3种
根据不同的同步机制,大致可以分3种,分别是:
1、锁:阻塞当前线程,等待一定条件,再往下走
2、串行队列:通过队列,把不同的数据操作代码,放在同一个线程执行
3、信号量:控制线程挂起/唤醒
下面分别来说说它们各自的工作原理。
锁
以自旋锁为例:
线程1走在lock与unlock之间时,
线程2走到lock时,会执行原地进行循环,转圈,并阻止代码往下走。
这样,线程2和1不会同时访问到同一片资源。等线程1访问完了,线程2就会跳出循环,再进行数据操作。
串行队列
不同的线程,把各自数据操作任务,都放入队列中。然后,队列依次把任务取出来,放到同一条线程中执行,实现同步
信号量
原理跟自旋类似。只不过是从循环变成线程挂起。
如果把它们两个比喻成过红绿灯的车子,自旋就是红灯时原地打圈,信号量就是红灯停车,等绿灯再通过。
两者相比,
自旋:一个不停运行的循环,消耗cpu计算资源,不需要唤醒,响应快
信号量:挂起与唤醒有时间开销,响应稍慢,但挂起节省cpu
iOS中的同步
上锁
简单的有NSLock,@synchronized()使用串行队列
dispatch_queue,dispatch代码块到同一个串行queue使用信号量
dispatch_semaphore,通过wait与signal控制
或者dispatch_group,同理。
最后说两句
一般的业务场景,客户端没有什么大并发,通常瓶颈不在于用什么锁,@synchronized()充以满足业务需求。
只有一点值得注意的是,@synchronized(一块地址),一地址对应一个锁。
常说@synchronized()性能差,很多时候是使用不规范。滥用@synchronized(self),相当于所有业务都使用的同一个锁。这意味着在有些时候业务A的进度,需要等待不相关的业务B,能不卡么。
正确的使用姿势是:合理地使用@synchronized( lock_obj1 / lock_obj2 ... ),不同业务使用不同的LockObject。