iOS开发之多线程(NSThread、NSOperation、GCD)

整理一些多线程相关的知识。

并行 & 并发

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 *timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(timeTask) userInfo:nil repeats:YES];
// [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[ 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
NSInvocationOperation *invocationOperation = [[ NSInvocationOperation alloc] initWithTarget: self selector: @selector(invocationOperation) object: nil];
[invocationOperation start];
//NSBlockOperation
NSBlockOperation * blockOperation = [ NSBlockOperation blockOperationWithBlock:^{
NSLog( @"blockOperation:%@",[ NSThread currentThread]);
}];
//这里面的任务是多线程执行的
[blockOperation addExecutionBlock:^{
NSLog( @"addExecutionBlock:%@",[ NSThread currentThread]);
}];
[blockOperation start];
- ( void)invocationOperation {
NSLog( @"invocationOperation:%@",[ NSThread currentThread]);
}
//invocationOperation:<NSThread: 0x60800007e1c0>{number = 1, name = main}
//blockOperation:<NSThread: 0x60800007e1c0>{number = 1, name = main}
//addExecutionBlock:<NSThread: 0x608000078800>{number = 3, name = (null)}

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
NSInvocationOperation *invocationOperation = [[ NSInvocationOperation alloc] initWithTarget: self selector: @selector(invocationOperation) object: nil];
//NSBlockOperation
NSBlockOperation * blockOperation = [ NSBlockOperation blockOperationWithBlock:^{
NSLog( @"blockOperation:%@",[ NSThread currentThread]);
}];
[operationQueue addOperation:invocationOperation];
[operationQueue addOperation:blockOperation];
- ( void)invocationOperation {
NSLog( @"invocationOperation:%@",[ NSThread currentThread]);
}
// invocationOperation:<NSThread: 0x60c00046b940>{number = 4, name = (null)}
// blockOperation:<NSThread: 0x60800026b580>{number = 3, name = (null)}

也可以直接添加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]);
}];
// <NSThread: 0x6080002637c0>{number = 4, name = (null)}
// <NSThread: 0x60c0002667c0>{number = 5, name = (null)}
// <NSThread: 0x60000047b2c0>{number = 3, name = (null)}

可以通过 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
NSBlockOperation * blockOperation0 = [ NSBlockOperation blockOperationWithBlock:^{
NSLog( @"blockOperation0:%@",[ NSThread currentThread]);
}];
NSBlockOperation * blockOperation1 = [ NSBlockOperation blockOperationWithBlock:^{
sleep( 1);
NSLog( @"blockOperation1:%@",[ NSThread currentThread]);
}];
//blockOperation0 依赖 blockOperation1,只有在 blockOperation1 执行完成才能执行 blockOperation0
[blockOperation0 addDependency:blockOperation1];
[operationQueue addOperation:blockOperation0];
[operationQueue addOperation:blockOperation1];
//blockOperation1:<NSThread: 0x600000269c80>{number = 3, name = (null)}
//blockOperation0:<NSThread: 0x60c000479040>{number = 4, name = (null)}

注意: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_syncdispat 大专栏  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);
});
//task0.....
//task1.....
//task2.....
//test....
//task3.....
//task4.....
//只有当task0、task1 执行完才会执行tast2.
//task2 执行完才会往下执行。
//test 执行完后,才会把task3、task4 加进队列.
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);
});
//test....
//task1.....
//task0.....
//task2.....
//task3.....
//task4.....
//test、task0、task1 并发执行。
//task0、task1 执行完才会执行tast2.
//tast2 执行完才会执行tast3、tast4.
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_after 会在到了指定的时间之后,将才将任务加到相应的队列中。
dispatch_once

一般会以这种形式出现

      
      
1
2
3
4
5
      
      
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog( @"dispatch_once task...");
});
//block里面的任务只会执行一次。
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");
// 0
// 1
// 3
// 2
// 4
// 5
// 6
// 7
// 8
// 9
// end
// dispatch_apply 会等待所有的任务完成了,才会往下执行。
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_notify(group, global, ^{
// NSLog(@"tast complete...");
//});
//会阻塞当前线程,直到 group 里面的任务全部完成
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog( @"wait");
// 异步加入的任务会立即执行
// dispatch_group_wait会阻塞当前线程,直到 group 里面的任务全部完成
// dispatch_group_enter & dispatch_group_leave 必须成对出现,相当于 dispatch_group_async

总结

当遇到相应场景的时候,知道使用哪种方法比较合理就行了。

猜你喜欢

转载自www.cnblogs.com/lijianming180/p/12326804.html