多线程大纲
9.1.同步/异步 和 串行/并发
问题一:同步/异步 和 串行/并发组合有哪些?
- dispatch_sync(serial_queue, ^{//任务});
- dispatch_async(serial_queue, ^{//任务});
- dispatch_sync(concurrent_queue, ^{//任务});
- dispatch_async(concurrent_queue, ^{//任务});
问题二:分析下面代码是否存在问题?
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"Hello world!");
});
}
- 会引起死锁
- 因为 Block 任务和 viewDidLoad 任务都要在主线程中执行,并且在同一个串行队列中,而这两个任务需要相互等待对方任务的完成才能继续进行,就造成了等待死锁。
问题三:分析下面代码是否存在问题?
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t serialQueue = dispatch_queue_create("apple.searial", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(@"Hello world!");
});
}
- 不会引起死锁
- Block 任务和 viewDidLoad 任务都要在主线程中执行,但是他们位于两个不同的串行队列中,而这两个任务不需要需要相互等待对方任务的完成就能继续进行,因此不会造成死锁。
问题四:分析下面代码是输出是什么?
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
- 1 2 3 4 5
- 问题的关键点在于并发队列,可以并发执行,即使是在同步的方式下进行,也不需要等待前面的任务,就不会造成死锁。
- global_queue 如果换成自定义的串行队列,就产生了和问题一相同的死锁。
问题五:下面代码熟悉吗?
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"这是我们开发中最常用的写法");
});
- 这是我们开发中最常用的写法
- 一般用于在其他子线程执行玩任务后,需要回到主线程进行 UI 绘制工作的时候用。
问题五:分析下面代码是输出是什么?
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"1");
[self performSelector:@selector(printLog) withObject:nil afterDelay:0];
NSLog(@"3");
});
}
- (void)printLog {
NSLog(@"2");
}
- 这里其实考察的是对 performSelector: withObject: afterDelay: 方法的理解。打印 1 3
- performSelector: withObject: afterDelay: 方法必须在有 runloop 的线程中调用,它需要把任务加到 runloop 中去,而全局并发队列中没有 runloop,所以,会直接失效。
9.2.dispatch_barrier_async()
问题一:怎么利用 GCD 实现多读单写?
流程分析
@implementation GlobalUserInfo
+ (GlobalUserInfo*) shareUserInfo {
static GlobalUserInfo *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
- (instancetype)init
{
self = [super init];
if (self) {
concurrentQueue = dispatch_queue_create("userinfo_read_write", DISPATCH_QUEUE_CONCURRENT);
userInfoDictionary = [[NSMutableDictionary alloc] init];
// 假设对年龄这个数据的多读单写
[userInfoDictionary setValue:@(1) forKey:@"age"];
}
return self;
}
- (int)getAge {
__block id obj;
// 同步读取指定数据
dispatch_sync(concurrentQueue, ^{
obj = [userInfoDictionary objectForKey:@"age"];
});
return [obj intValue];
}
- (void)setAge:(int)age {
// 异步栅栏调用设置数据
dispatch_barrier_async(concurrentQueue, ^{
[self->userInfoDictionary setValue:@(age) forKey:@"age"];
// 模拟耗时的写操作,才能看到效果
[NSThread sleepForTimeInterval:2.0];
NSLog(@"write queue4 %i", age);
});
}
@end
9.3.dispatch_group_async()
问题一:怎么利用 GCD 实现这个需求:A、B、C 三个任务并发,完成后执行任务 D?
- (void)learnGCDGroup {
// 模拟真实开发中多个数据资源需要下载
NSMutableArray *urlArray = [[NSMutableArray alloc] init];
for (int i = 0 ; i < 10; i++) {
[urlArray addObject:@(i)];
}
// 创建一个group
dispatch_group_t group = dispatch_group_create();
// 创建一个并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("download_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
// 进行并发图片下载
for (id obj in urlArray) {
// 模拟耗时下载
dispatch_group_async(group, concurrentQueue, ^{
int randomTime = arc4random()% 3;
[NSThread sleepForTimeInterval:randomTime];
NSLog(@"download resource finished %@",obj);
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"所有资源已经下载完毕,可以再主线程更新 UI 了.");
});
}
9.4.NSOperationQueue
问题一:哪些特点的方法使用 NSOperation 更为方便呢?
问题二:我们可以控制 NSOperation 的哪些状态?
- isReady
- isExecuting
- isFinished
- isCancelled
问题三:从代码的角度,我们如何控制 NSoperation 的状态?
- 如果只重写了 main 方法,底层控制变更任务执行完成状态,以及任务退出。
- 如果重写了 start 方法,自行控制任务状态。
问题四:系统是怎样移除一个 isFinished=YES 的 NSOperation 的?
9.6.多线程 和 锁
问题一:在 iOS 当中有哪些锁呢?或者说你使用过哪些锁?
- @synchoroinized(一般在创建单例对象的时候使用)
- atomic(对被修饰对象进行原子操作,不负责使用)
- OSSpinLock(循环等待访问,不释放当前资源,用于轻量级数据访问,简单的 int 值+1/-1 操作)
- NSRecursiveLock(适合在递归操作中使用)
- NSLock
- dispatch_semaphore_t
网友评论