前言ios
项目中使用多线程,首要考虑的问题就是线程安全问题,广泛存在的是多个线程同时访问同一内存(存在读写操做,单独的读操做是不会发生冲突的)发生的数据冲突或者是crash。每每咱们的解决方案通常采用同步队列或者是加锁,可是要考虑死锁问题。swift
下面解释几个名词;安全
(1)队列类型网络
同步队列:一个一个按照FIFO的执行任务,后一个任务须要等待前一个任务执行完成后才能被执行;多线程
并发队列:后一个任务不须要等待前一个任务执行完成就能够被执行,如果异步提交任务到异步队列则能够并发执行多个任务,如果同步提交任务则是一个一个执行任务。并发
(2)执行方式异步
同步方式会阻塞当前线程,异步方式不会阻塞当前线程(当即返回)。ui
(3)加锁spa
对于锁的解释网络上有不少文章,看下面几篇文章线程
非递归锁,如NSLock等;
递归锁:同一个线程能够屡次加锁而不会形成死锁,但会阻塞其余线程访问(lock与unlock成对出现)。官方文档对递归锁NSRecursiveLock的解释;
A recursive lock is a variant on the mutex lock. A recursive lock allows a single thread to acquire the lock multiple times before releasing it. Other threads remain blocked until the owner of the lock releases the lock the same number of times it acquired it. Recursive locks are used during recursive iterations primarily but may also be used in cases where multiple methods each need to acquire the lock separately.
容易出现死锁的状况;
(1)使用队列形成的死锁,同一线程屡次同步提交任务到同步队列,同步执行方式会阻塞当前线程,而同步队列想要执行后一个任务必需要等到前一个任务完成后,后一个任务才会被执行,形成先后任务相互等待线程休眠,最后致使死锁。比较经典的死锁的例子;
dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"任务二"); }); NSLog(@"任务一");
因为同步执行方式阻塞当前线程,任务一等待任务二执行完才可以执行,可是因为任务二被添加到任务一以后,因此任务二需等待任务一执行完才能被执行,因而形成了死锁。
(2)使用加锁形成的死锁,使用非递归锁在没有被解锁的状况下屡次加锁则会形成加锁死锁,缘由是未解锁屡次加锁会出现前一次加锁想没法解锁,后一次加锁获取不到锁对象的状况,像队列同样,会形成相互等待线程休眠的状况,从而致使死锁。看一个例子;
NSLock *theLock = [[NSLock alloc] init]; //NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init]; void MyRecursiveFunction(int value) { [theLock lock]; if (value != 0) { --value; MyRecursiveFunction(value); } [theLock unlock]; } MyRecursiveFunction(5);
上述代码是在递归调用中使用非递归锁加锁,会形成加锁死锁,改为递归锁后正常执行。
总结
使用多线程不只要考虑线程安全,还须要考虑死锁的状况,今天只是分析如下两种致使死锁的状况;
(1)同步提交任务至同步队列;
(2)在有递归或者是循环状况下,使用非递归锁加锁。