队列与线程的关系
-
『ios』主线程 和 主队列的关系,绝对安全的UI操作,主线程中一定是主队列?
- (void)testInMainThread { // 异步提交任务到主队列 dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"1 --- %@", [NSThread currentThread]); self.label.backgroundColor = [UIColor orangeColor]; // 正常 }); // 同步提交任务到自定义并发队列 dispatch_sync(dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT), ^{ NSLog(@"2 --- %@", [NSThread currentThread]); self.label.backgroundColor = [UIColor greenColor]; // 正常 }); // 同步提交任务到自定义串行队列 dispatch_sync(dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL), ^{ NSLog(@"3 --- %@", [NSThread currentThread]); self.label.backgroundColor = [UIColor whiteColor]; // 正常 }); } 2 --- <NSThread: 0x6000039a1400>{number = 1, name = main} 3 --- <NSThread: 0x6000039a1400>{number = 1, name = main} 1 --- <NSThread: 0x6000039a1400>{number = 1, name = main}
- 队列不是线程。队列用来组织任务,线程是执行队列中的任务。
- 主队列是一个特殊的串行队列,异步提交任务到主队列不会开辟新线程。
- 一个线程可以执行多个队列中的任务。
- 只要在主线程,主队列、自定义的串行/并发队列任务都能够刷新 UI。
- 主队列不一定在主线程执行,比如:人为调用 dispatch_main()。
死锁
用 dispatch_sync 添加任务到它自己所在的
串行队列
中就会造成死锁。
死锁的三个条件:
- dispatch_sync(同步)添加任务
- 串行队列
- 添加的任务和 dispatch_sync 在同一个队列
- (void)testInMainThread
{
// 情形 ①、dispatch_sync 自身在主队列
dispatch_sync(dispatch_get_main_queue(), ^{
});
dispatch_queue_t queue = dispatch_queue_create("CONCURRENT", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// 情形 ②、dispatch_sync 自身在 queue 队列
dispatch_sync(queue, ^{
NSLog(@"222");
});
});
}
解决方案就是让死锁的三个条件中任意一个不满足即可。
读写安全方案
- 同一时间,只允许一个线程执行写入操作
- 同一时间,许多个线程都执行读取操作
- 同一时间,不能既有写入操作,也有读取操作
方案:
- dispatch_semaphore 信号量,缺陷是不能实现允许多个线程读取操作
- dispatch_barrier_async 栅栏,注意读写任务队列必须是自己创建的并发队列
- pthread_rwlock 读写锁
数据竞争
多个线程并发执行,同一时间访问同一块数据(如同一个变量、对象、文件等),就有可能出现数据竞争,从而导致数据错乱。
线程同步
方案:
- 加锁。多个线程使用同一把锁,对访问数据的关键代码进行加锁;
- OSSpinLock 自旋锁
- os_unfair_lock 互斥锁
- pthread_mutex 互斥锁
- NSLock 互斥锁
- NSRecursiveLock 递归锁
- @synchronized
- 设置线程的最大并发数为 1
- 把多个线程放入串行队列
其中方案 2 和 3 差不多。