iOS 线程同步几种方式

多线程同步目的有以下几个方面:第一,对一段代码的执行进行保护,如果同时执行一段代码,中间的临时变量可能会互相干扰造成结果不对;第二,对资源的保护,多个线程执行不同的代码,但是可能涉及同一个资源;第三,消息传递,一个线程通知另外一个线程发生了一件事。

iOS中常用线程同步方式:

NSLock

一个封装了pthread_mutex的OC对象,常用于保护一个代码块,如果NSLock对象已经被lock,当前线程放弃时间片并阻塞,等待锁的释放。

可以通过lockBeforeDate:限制一个锁定的时间,获取锁的时候也可以使用tryLock:来避免阻塞。

NSLock有3点要注意:unlock的执行线程必须和lock相同,否则行为未定义;在同一个线程上lock两次会造成死锁;向未lock的对象发送unlock会造成Crash。

NSRecursiveLock

NSRecursiveLock也是封装了一个pthread_mutex,和NSLock不同的是在NSLock中pthread_mutex的类型是 PTHREAD_MUTEX_ERRORCHECK,而在NSRecursiveLock中pthread_mutex的类型是PTHREAD_MUTEX_RECURSIVE。

在同一个线程上lock两次会造成死锁,但是一个线程在请求锁之前可能不知道这个锁是否已经被锁定,亦或一个函数可能会被多次或者递归调用,如果贸然加锁,就会造成死锁,此种情况下,应该使用NSRecursiveLock。NSRecursiveLock允许一个线程多次获取锁(同时需要相应的释放次数),对于没有获取锁的线程而言,与NSLock功能一致。

NSConditionLock & NSCondition

NSLock是比较简单的锁,一个线程申请锁,然后其他线程等待,当这个线程释放锁的时候,等待的线程哪个会被唤醒没有定义。如果仍然使用NSLock,此时的解决方案就是线程在释放锁之前设置一个全局的状态,然后等待的线程获取锁之后,查看状态,如果不是自己要处理的情况,就释放锁,重新进入循环。但是这种方案有不可预测性,因为不知道应该被唤起的线程何时能获取到锁,其他线程不停的被唤起等待性能损失也非常大。

NSConditionLock就是解决这种情况的。NSConditionLock把条件变量封装到了锁中,内核根据是否满足等待条件来决定是否让线程获得锁。有两个核心API:

-lockWhenCondition:如果满足条件,就获取锁。

-unlockWithCondition:设置一个获取锁的条件并解锁。

dispatch_semaphore_t

考虑另外一种场景,一个线程是生产者,生产了3个产品,然后有5个消费者线程等待。这是介于NSLock和NSConditionLock之间的场景,第一是有条件的,一共就3个产品;第二,不用区分线程,只要唤醒3个就可以。这时候无论使用NSLock还是使用NSConditionLock都不太方便。

dispatch_semaphore_t就是在这样的场景下使用。dispatch_semaphore_t解决方案有3个核心函数:

dispatch_semaphore_create,创建一个信号量,指定资源个数。

dispatch_semaphore_wait,等待信号量,如果资源已经为0则阻塞线程,当资源大于0的时候被唤醒。

dispatch_semaphore_signal,发送一个信号量,将资源数+1。

这里还有一个逻辑,就是如果资源数已经达到了初识设定的3个,生产者线程再signal也会被阻塞。在实际工程中,这个机制可以保护产品队列的大小。

dispatch_barrier_async & dispatch_barrier_sync

还有一种场景,就是多个线程访问一个资源,对于读可以并发,但是对于写需要独占,也就是所谓的读写锁。

使用GCD的API可以实现这个需求。把一个block使用barrier的方式提交到并行队列中,一旦这个block开始执行,队列中其他的block就不能执行直到这个block执行完成。可以把写操作写在这个block中,然后读操作写在另外的block中使用一般的提交。就能实现读并发,写独占。

dispatch_barrier_async和dispatch_barrier_sync的区别是dispatch_barrier_sync会阻塞线程,直到提交的block执行完毕。

需要注意的是,要自己创建并行队列,官方文档给出:

The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_async function.

其实,对于读写锁,更好理解的是pthread_rwlock系列函数。

你可能感兴趣的:(ios开发)