iOS提供了三种多线程的调用,分别是NSThread、NSOperationQueue、及GCD,三者使用的轻重度依次是GCD、NSOperationQueue、NSThread,这里就不详细对比。GCD这要是采用C语言语法配合Block实现,可以实现同步、异步操作;运行并行、串行队列;同步锁,单例、延时等。
主线程队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
全局并发队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
自定义队列
dispatch_queue_t serialQueue = dispatch_queue_create("SERIAL", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("CONCURRENT", DISPATCH_QUEUE_CONCURRENT);
Serial Dispatch Queue | 串行队列 |
Concurrent Dispatch Queue | 并行队列 |
队列的应用场景
在使用 GCD 时,可以根据任务的性质和需求来选择是使用全局并发队列还是自定义的队列。
使用全局并发队列的情况:
-
同时执行多个独立的任务,全局并发队列能够充分利用系统资源。
-
执行大量的计算或者涉及大量的 I/O 操作,全局并发队列能够更好地利用系统资源,并根据系统情况动态调整并发数。
-
使用全局并发队列中的高优先级队列,确保这些任务得到优先处理,提高响应速度。
使用自定义队列的情况:
-
需要按照特定的顺序执行任务,使用自定义的串行队列。
-
需要对并发执行的任务进行更精细的控制(组合、栏栅化等),使用自定义的并发队列。
总的来说,全局并发队列适用于大多数常见的并发任务,而自定义队列更适合满足特定需求、对队列行为进行更精细控制或实现任务之间的依赖关系。
async/sync
async为异步操作,不用等待执行结果,sync为同步操作,等待执行结果。
async可以异步开启多线程队列,包括串行队列、并行队列,也可以异步返回主线程刷新UI:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//多线程操作
dispatch_async(dispatch_get_main_queue(), ^{
//主线程刷新UI
});
});
sync一般常配合串行队列实现同步锁:
dispatch_queue_t serialQueue = dispatch_queue_create("QUEUESERIAL", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
// 同步操作
});
同步锁不能用于该串行队列的线程操作中,不然会导致死锁。
suspend/resume
将指定的队列挂起:
dispatch_suspend(queue);
恢复指定的队列执行:
dispatch_resume(queue);
dispatch_group
GCD中的Group可以解决异步队列的数据不同步问题,实现同个并行队列的回调监听,并在队列执行完之后调用notify通知。
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("CONCURRENTQUEUE", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(_group, _queue, ^{ sleep(2); NSLog(@"1"); });
dispatch_group_async(_group, _queue, ^{ sleep(2); NSLog(@"2"); });
dispatch_group_notify(_group, dispatch_get_main_queue(), ^{ NSLog(@"3"); });
如果不是用同一个GCD_Queue的多线程操作,可以用group_enter和group_leave实现,类似于计数器。
dispatch_group_enter(_group); //+1
dispatch_group_leave(_group); //-1
dispatch_group_notify(_group, dispatch_get_main_queue(), ^{ });//为0时触发通知回调
group中的wait方法也会阻塞线程,但也随着enter和leave的平衡(计数为0时)而失效。
dispatch_group_wait(_group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)));//阻塞线程2秒钟
dispatch_barrier
在并行队列中添加串行操作,实现栏栅化。
self.queue = dispatch_queue_create("com.barrier.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(_queue, ^{
sleep(1);
NSLog(@"1");
});
dispatch_async(_queue, ^{
sleep(1);
NSLog(@"2");
});
dispatch_barrier_async(_queue, ^{
sleep(1);
NSLog(@"3");
});
dispatch_barrier_async(_queue, ^{
sleep(1);
NSLog(@"4");
});
dispatch_async(_queue, ^{
sleep(1);
NSLog(@"5");
});
dispatch_async(_queue, ^{
sleep(1);
NSLog(@"6");
});
// 先1、2并行,再3、4串行,最后5、6并行
在实际项目中,主要用作读写锁的功能-多读单写。
self.datas = [NSMutableDictionary dictionary];
self.rwLock = dispatch_queue_create("com.rwLock.queue", DISPATCH_QUEUE_CONCURRENT);
// 读取操作
- (NSString *)readData {
__block NSString *name = nil;
dispatch_sync(_rwLock, ^{
name = [self.datas objectForKey:@"name"];
});
return name;
}
// 写入操作
- (void)writeData {
dispatch_barrier_async(_rwLock, ^{
[self.datas setObject:@"PeterLiu" forKey:@"name"];
});
}
// 读写操作
- (NSString *)readWriteData {
__block NSString *identify = nil;
dispatch_barrier_sync(_rwLock, ^{
identify = [NSString stringWithFormat:@"%@-027", [self.datas objectForKey:@"name"]];
[self.datas setObject:identify forKey:@"identify"];
});
return identify;
}
// 使用sync或async,取决于是否等待执行结果
dispatch_semaphore
通过设置信号量限定当前访问资源的最大线程量,先调用wait(降低)再调用signal(提高),信号量小于0时,处于锁住状态,无法访问资源。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任务1
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 1");
sleep(1);
NSLog(@"complete task 1");
dispatch_semaphore_signal(semaphore);
});
//任务2
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});
//任务3
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 3");
sleep(1);
NSLog(@"complete task 3");
dispatch_semaphore_signal(semaphore);
});
}
// 信号量为1,依次执行Task1、Task2、Task3。
把异步任务转为同步任务,类似与dart / js的await;在异步任务未结束时,当前调用线程会被阻塞,避免在主线程中直接调用。
// 异步执行任务转换为同步执行任务
- (NSString *)obtainDatas {
__block NSString *datas = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
datas = @"sync_data";
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return datas;
}
dispatch_once
单例模式,确保应用只执行一次once中的代码块。
static NSArray* datas = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
datas = @[@"1", @"2", @"3"];
});
dispatch_after
延时处理,类似于NSTimer,不过只是单次,不能设置循环。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//延时两秒
});
dispatch_source
事件源处理,一般应用于定时器,具有高精准度,不会受到RunLoopMode切换的影响(Default-Tracking)。NSTimer(有一定的误差)、CADisplayLink(高精准度,适用于高频的动画效果),NSTimer与CADisplayLink都会受到RunLoopMode切换的影响。
@implementation GCDSource
- (instancetype)initWithTimeInterval:(NSTimeInterval)timeInterval repeats:(BOOL)repeats timerBlock:(void(^)(void))timerBlock {
self = [super init];
if (self) {
_timeInterval = timeInterval;
_repeats = repeats;
self.timerBlock = timerBlock;
[self initTimer];
}
return self;
}
- (void)initTimer {
dispatch_queue_t timerQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t blockQueue = dispatch_get_main_queue();
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timerQueue);
dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), _timeInterval * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(_timer, ^{
dispatch_async(blockQueue, ^{
self.timerBlock();
});
if (_repeats == NO) {
[self stopTimer];
}
});
dispatch_resume(_timer);
}
- (void)pauseTimer {
if(self.timer){
dispatch_suspend(_timer);
}
}
- (void)resumeTimer {
if(self.timer){
dispatch_resume(_timer);
}
}
- (void)stopTimer {
if(self.timer){
dispatch_source_cancel(_timer);
_timer = nil;
}
}
- (void)dealloc {
[self stopTimer];
}
@end
dispatch_target
使队列优先级与目标队列相同,如果将多个串行的queue使用dispatch_set_target_queue指定到了同一目标,那么着多个串行queue在目标queue上就是同步执行的,不再是并行执行。
dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("queue3", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_async(queue1, ^{
sleep(1);
NSLog(@"1");
});
dispatch_async(queue2, ^{
sleep(1);
NSLog(@"2");
});
dispatch_async(queue3, ^{
sleep(1);
NSLog(@"3");
});
三个不同的串行队列,会作为一个串行队列的方式执行,依次输出1,2,3。
GCD对象化封装:GCDObjective