why need sync
在多线程并发的情况下,一个不注意,非常容易因为并发操作数据,导致各种各样的Bug。相信作为一名开发者,应该也遇到过这样的问题。
那么如何能用正确的姿势,在保证不出Bug的前提下,享受多线程带来的效率呢?
3种同步机制
所有的同步方式,根据不同的同步机制,大致可以分3种,分别是:
1、自旋:通过循环,阻塞当前线程,等待一定条件,再跳出循环往下走
2、串行队列:把任务通过队列,扔到一个线程里面执行,都一个线程了就不会有并发问题
3、信号量:线程的睡眠,与唤醒
下面就来了解下它们的工作原理吧,也更方便以后使用起来更加得心应手。
1、自旋
作为一名合格的画手,上图:

当线程1在lock与unlock之间的时候,
线程2走到lock时,会执行原地进行循环,转圈,阻止代码往下走。
这样,线程2与1就不会同时访问到同一片资源,也就不会产生数据不同步问题。
等1访问完了,2就会跳出循环,再进行它自己的数据操作。
2、串行队列
队列很多系统都会有,这种消息队列,会把队列中一个一个任务,放到同一条线程去执行。
把在不同的线程的数据操作任务,通过dispatch到一个队列,再放到一条单线程中执行,实现同步

3、信号量
原理跟自旋类似。只不过是从循环变成线程睡眠sleep。如果把它们两个比喻成过红绿灯的车子,自旋就是红灯时原地打圈,信号量就是红灯停车,等绿灯再通过。
两者相比,
线程的睡眠与唤醒,有额外的空间与时间开销,意味着响应稍慢,但睡眠节省cpu
而自旋,是循环,消耗cpu计算资源,不需要唤醒,意味着响应快,对于app也意味着更加耗电与发热
在iOS中的使用
-
串行队列
搜索关键字:dispatch_queue_t,把相要同步操作的代码,dispatch到同一个queue即可 -
信号量
搜索关键字:dispatch_semaphore,一个信号一个坑 -
锁
最简单的NSLock,@synchronized
有几点值得注意的:
1、追求高效的,可以使用读写锁pthread_rwlock,具体用法网上都有,可以支持读并发,写串行。听起来很帅,实际上也很帅
2、为什么很多说使用@synchronized性能会差,会卡,因为@synchronized(对象,有地址就行),一个地址对应一个锁。不管三七二十一任何情况都用@synchronized(self),原本不需要同步的操作,不需要互为原子的操作,都不能进行并发了,导致慢卡。
所以,还是需要不同场景,合理使用@synchronized(obj1/obj2/obj3..)。这几个用于锁的obj,随便new几个就可以了,不需要必须是self
网友评论
1、关于读写锁和互斥锁如你所说,没有特别的要求, 使用互斥锁就够了。很多时候客户端,对于数据处理是读多于写的,多线程读与读之间,不存在数据不同步的问题,所以不上锁肯定比上锁性能来得要高。
2、并于其次,从文章出发点,侧重点不在于锁,锁是实现同步的手段之一,更多的讲的是,同步,它是“怎么做到的”,可以通过锁/自旋算法/队列/信号量,根据不同场景,采取不同的手段。偏原理分析向的,关于落地到具体怎么做这点,可能没有你期望的详细教程,这些搜索引擎上面也是有很多好文章的~可以参考一下