整理一些多线程相关的知识。
并行 & 并发
1、并行:并行是相对于多核而言的,几个任务同时执行。
2、并发:并发是相对于单核而言的,几个任务之间快速切换运行,看起来像是“同时”发生的一样
NSThread
优点:轻量级
缺点:需要手动管理线程活动,如生命周期、线程同步、睡眠等。
搭配runloop实现常驻线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil]; [thread start]; - (void)threadRun { @autoreleasepool { NSLog(@"threadRun"); [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(timeTask) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] run]; } } - (void)timeTask { NSLog(@"timeTask:%@",[NSThread currentThread]); }
|
NSOperation & NSOperationQueue
NSOperation
NSOperation 是一个抽象类,只能使用它的自类来进行操作。系统为我们创建了两个子类NSInvocationOperation & NSBlockOperation。
直接使用这两个类执行任务,系统不会创建子线程,而是在当前线程执行任务。NSBlockOperation 使用 addExecutionBlock方法的任务是在多线程执行的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil]; [invocationOperation start]; NSBlockOperation * blockOperation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"blockOperation:%@",[NSThread currentThread]); }]; [blockOperation addExecutionBlock:^{ NSLog(@"addExecutionBlock:%@",[NSThread currentThread]); }]; [blockOperation start]; - (void)invocationOperation { NSLog(@"invocationOperation:%@",[NSThread currentThread]); }
|
NSOperationQueue
//主队列,任务在主线程执行,一般用于更新UI的时候使用。
NSOperationQueue queue = [NSOperationQueue mainQueue];
//子队列,任务在子线程执行,用于处理耗时任务。
NSOperationQueue queue = [[NSOperationQueue alloc] init];
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil]; NSBlockOperation * blockOperation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"blockOperation:%@",[NSThread currentThread]); }]; [operationQueue addOperation:invocationOperation]; [operationQueue addOperation:blockOperation]; - (void)invocationOperation { NSLog(@"invocationOperation:%@",[NSThread currentThread]); }
|
也可以直接添加block
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; [operationQueue addOperationWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }]; [operationQueue addOperationWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }]; [operationQueue addOperationWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }];
|
可以通过 maxConcurrentOperationCount 来控制最大并发数,当最大并发数为1时,就相当于串行队列
NSOperation添加依赖关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; NSBlockOperation * blockOperation0 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"blockOperation0:%@",[NSThread currentThread]); }]; NSBlockOperation * blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{ sleep(1); NSLog(@"blockOperation1:%@",[NSThread currentThread]); }]; [blockOperation0 addDependency:blockOperation1]; [operationQueue addOperation:blockOperation0]; [operationQueue addOperation:blockOperation1];
|
注意:NSOperationQueue 是非线程安全的,多个线程访问统一资源时,需要加锁。
1 2 3 4 5
| self.lock = [[NSLock alloc] init]; [self.lock lock]; [self.lock unlock];
|
GCD
创建队列,然后再往队列添加任务。也可以直接使用系统创建的队列。
1 2 3 4 5 6 7 8
| dispatch_queue_t queue = dispatch_queue_create("com.vhuichen.queue", DISPATCH_QUEUE_SERIAL); dispatch_queue_t queue = dispatch_queue_create("com.vhuichen.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
创建同步、异步任务
1 2 3 4 5 6 7
| dispatch_sync(queue, ^{ }); dispatch_async(queue, ^{ });
|
GCD常用方法
dispatch_barrier
只有当添加在dispatch_barrier前面的任务完成了,才开始执行dispatch_barrier任务。
有两种方式:dispatch_barrier_sync 和 dispat 大专栏 iOS开发之多线程(NSThread、NSOperation、GCD)ch_barrier_async
dispatch_barrier_sync
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| dispatch_queue_t queue = dispatch_queue_create("com.vhuichen.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"task0....."); sleep(1); }); dispatch_async(queue, ^{ NSLog(@"task1....."); sleep(1); }); dispatch_barrier_sync(queue, ^{ NSLog(@"task2....."); sleep(5); }); NSLog(@"test"); dispatch_async(queue, ^{ NSLog(@"task3....."); sleep(2); }); dispatch_async(queue, ^{ NSLog(@"task4....."); sleep(1); });
|
dispatch_barrier_async
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| dispatch_queue_t queue = dispatch_queue_create("com.vhuichen.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"task0....."); sleep(1); }); dispatch_async(queue, ^{ NSLog(@"task1....."); sleep(1); }); dispatch_barrier_async(queue, ^{ NSLog(@"task2....."); sleep(5); }); NSLog(@"test"); dispatch_async(queue, ^{ NSLog(@"task3....."); sleep(2); }); dispatch_async(queue, ^{ NSLog(@"task4....."); sleep(1); });
|
dispatch_after
1 2 3 4
| dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"dispatch_after task..."); });
|
dispatch_once
一般会以这种形式出现
1 2 3 4 5
| static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"dispatch_once task..."); });
|
dispatch_apply
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| dispatch_queue_t queue = dispatch_queue_create("com.vhuichen.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_apply(10, queue, ^(size_t index) { NSLog(@"%zu",index); }); NSLog(@"end");
|
dispatch_semaphore
可以用于线程同步、线程加锁、控制并发线程数量
1 2 3 4 5 6 7 8 9 10
| let semaphoreSignal = DispatchSemaphore(value: 0) ZBDepthAPI.GET(market: market.name!, succeed: { (depthModel) in semaphoreSignal.signal() }) { (error) in semaphoreSignal.signal() } semaphoreSignal.wait()
|
dispatch_group
当需要同时执行多个耗时任务,并且当所有任务执行完后更新UI。那么这时可以使用 dispatch_group 来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| dispatch_group_t group = dispatch_group_create(); dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_async(group, global, ^{ NSLog(@"tast0......."); }); NSLog(@"0"); dispatch_group_async(group, global, ^{ NSLog(@"tast1......."); }); NSLog(@"1"); dispatch_group_async(group, global, ^{ NSLog(@"tast2......."); sleep(1); }); NSLog(@"2"); dispatch_group_enter(group); dispatch_async(global, ^{ NSLog(@"tast00......."); dispatch_group_leave(group); }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); NSLog(@"wait");
|
总结
当遇到相应场景的时候,知道使用哪种方法比较合理就行了。