Fork me on GitHub

pthread、NSThread、GCD、NSOperation、NSOperationQueue

pthread、NSThread

GCD

基本介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
// 并发队列
dispatch_queue_t queue_t = dispatch_queue_create("ccc", DISPATCH_QUEUE_CONCURRENT);
// 串行队列
dispatch_queue_t queue_t1 = dispatch_queue_create("ccc", DISPATCH_QUEUE_SERIAL);
// 串行队列 (主队列)
dispatch_get_main_queue();
// 全局并发队列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 同步
dispatch_sync
// 异步
dispatch_async

线程通讯

1
2
3
4
// gcd 线程通讯一般只有 子线程通讯到主线程,
// 因为我们在处理gcd的时候没有操作具体的线程,
// 全部是由系统来处理的
dispatch_async(mainQueue, ^{

延时操作

1
2
// 延时操作 这里只是延时把任务加到队列中,具体什么时候执行是未知的。
// dispatch_after

只执行一次

1
2
// 只执行一次
dispatch_once(&onceToken, ^{

快速迭代方法

1
2
3
// 快速迭代方法,普通遍历数组的时候是一个一个取,
// 如果用快速迭代的话就可以在多个线程中同时(异步)遍历。
dispatch_apply(6, queue, ^(size_t index) {

栅栏

1
2
3
4
5
6
7
// 栅栏 可以把任务栅起来特殊处理,指定哪些任务完成后在执行哪些任务
// 1 2 完成了再继续这些 3 4 5任务
dispatch_async(queue, ^{
dispatch_async(queue, ^{
dispatch_barrier_async(queue, ^{
dispatch_async(queue, ^{
dispatch_async(queue, ^{

队列组

通知方式

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
38
39
40

- (void)dispatch_group_notify {
// 1、创建队列组
dispatch_group_t group = dispatch_group_create();
// 2、创建队列,全局并发队列
dispatch_queue_global_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

printf("\nstar %d %s", __LINE__, [NSThread currentThread].description.UTF8String);

// 添加任务
dispatch_group_async(group, queue, ^{
sleep(1);
printf("\n%d %s", __LINE__, [NSThread currentThread].description.UTF8String);
});

// 添加任务
dispatch_group_async(group, queue, ^{
sleep(3);
printf("\n%d %s", __LINE__, [NSThread currentThread].description.UTF8String);
});

// 添加任务
dispatch_group_async(group, queue, ^{
sleep(2);
printf("\n%d %s", __LINE__, [NSThread currentThread].description.UTF8String);
});

// 添加任务,此任务需要等 group 中的全部任务执行完毕才执行
dispatch_group_notify(group, queue, ^{
printf("\n%d %s", __LINE__, [NSThread currentThread].description.UTF8String);
});
printf("\nend %d %s", __LINE__, [NSThread currentThread].description.UTF8String);

// star 34 <NSThread: 0x10120baa0>{number = 1, name = main}
// end 58 <NSThread: 0x10120baa0>{number = 1, name = main}
// 39 <NSThread: 0x10111a670>{number = 4, name = (null)}
// 51 <NSThread: 0x101140910>{number = 3, name = (null)}
// 45 <NSThread: 0x101161df0>{number = 5, name = (null)}
// 56 <NSThread: 0x101161df0>{number = 5, name = (null)}
}

堵塞

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
- (void)dispatch_group_wait {
// 1、创建队列组
dispatch_group_t group = dispatch_group_create();
// 2、创建队列,全局并发队列
dispatch_queue_global_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

printf("\nstar %d %s", __LINE__, [NSThread currentThread].description.UTF8String);

// 添加任务
dispatch_group_async(group, queue, ^{
sleep(1);
printf("\n%d %s", __LINE__, [NSThread currentThread].description.UTF8String);
});

// 添加任务
dispatch_group_async(group, queue, ^{
sleep(3);
printf("\n%d %s", __LINE__, [NSThread currentThread].description.UTF8String);
});

// 添加任务
dispatch_group_async(group, queue, ^{
sleep(2);
printf("\n%d %s", __LINE__, [NSThread currentThread].description.UTF8String);
});

// 到这里就堵塞当前线程,当 group 的任务全部完成在继续执行
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
printf("\nend %d %s", __LINE__, [NSThread currentThread].description.UTF8String);
// star 35 <NSThread: 0x100e014b0>{number = 1, name = main}
// 40 <NSThread: 0x100d617d0>{number = 3, name = (null)}
// 52 <NSThread: 0x100ed7d30>{number = 6, name = (null)}
// 46 <NSThread: 0x100ed8710>{number = 7, name = (null)}
// end 55 <NSThread: 0x100e014b0>{number = 1, name = main}
}

标记的方式

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
38
39
40
41
42
43
44
45
46
47
48
// 开始一个任务打标记
dispatch_group_enter(group);
// 结束一个任务打标记
dispatch_group_leave(group);
// 全部完成了通知指定回调
dispatch_group_notify
- (void)dispatch_group_enter {
// 1、创建队列组
dispatch_group_t group = dispatch_group_create();
// 2、创建队列,全局并发队列
dispatch_queue_global_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

printf("\nstar %d %s", __LINE__, [NSThread currentThread].description.UTF8String);

// 使用标记的方式
// 标记开始一个,标记结束一个
dispatch_group_enter(group);
dispatch_async(queue, ^{
sleep(1);
printf("\n%d %s", __LINE__, [NSThread currentThread].description.UTF8String);
dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_async(queue, ^{
sleep(2);
printf("\n%d %s", __LINE__, [NSThread currentThread].description.UTF8String);
dispatch_group_leave(group);
});

printf("\nend1 %d %s", __LINE__, [NSThread currentThread].description.UTF8String);

// 等 group 的任务全部完成在执行 block 中的任务
// 使用 dispatch_group_enter 和 直接使用通知的方式差不多
// 只是添加任务的方式不一样
// 通知的方式是使用 dispatch_group_async 添加任务
// 标记发方式是使用 dispatch_async 添加任务
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
printf("\nend3 %d %s", __LINE__, [NSThread currentThread].description.UTF8String);
});
printf("\nend2 %d %s", __LINE__, [NSThread currentThread].description.UTF8String);
// star 32 <NSThread: 0x107e0b8e0>{number = 1, name = main}
// end1 50 <NSThread: 0x107e0b8e0>{number = 1, name = main}
// end2 55 <NSThread: 0x107e0b8e0>{number = 1, name = main}
// 39 <NSThread: 0x107ddebc0>{number = 3, name = (null)}
// 46 <NSThread: 0x107de3c70>{number = 5, name = (null)}
// end3 53 <NSThread: 0x107e0b8e0>{number = 1, name = main}
}

信号量

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
// 创建信号量
// dispatch_semaphore_create(n)

// 内部实现
// 首先把信号量减1,得到结果
// if >= 0 继续执行任务
// else 调用系统内核函数来休眠当前线程
// dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

// 释放号码量
// 把信号量 +1
// 唤醒线程,这里使用的是先进先出 FIFO 的方式,先休眠的先唤醒
// dispatch_semaphore_signal(semaphore);

- (void)dispatch_semaphore_t {

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
printf("\nstar %d %s", __LINE__, [NSThread currentThread].description.UTF8String);
dispatch_async(dispatch_get_global_queue(0, 0), ^(void) {
sleep(1);
printf("\n任务%d %s", __LINE__, [NSThread currentThread].description.UTF8String);
// 信号量+1,唤醒线程
dispatch_semaphore_signal(semaphore);
});

printf("\nend1 %d %s", __LINE__, [NSThread currentThread].description.UTF8String);

// 内部实现把信号量-1,此时为 -1 ,< 0 就调用系统内核函数休眠线程
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
printf("\nend2 %d %s", __LINE__, [NSThread currentThread].description.UTF8String);
}

NSOperation、NSOperationQueue

NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。

为什么要使用 NSOperation、NSOperationQueue?

  • 可添加完成的代码块,在操作完成后执行。

  • 设定操作执行的优先级。

  • 可以很方便的取消一个操作的执行。

  • 使用 KVO 观察对操作执行状态的更改:isExecuteing、isFinished、isCancelled。

创建操作

NSOperation 的创建

NSInvocationOperation

1
2
3
4
5
// 在当前线程上执行
// 1.创建 NSInvocationOperation 对象
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
// 2.调用 start 方法开始执行操作
[op start];

NSBlockOperation

1
2
3
4
5
6
7
8
9
10
// 各种 Block 可以在多个线程上执行
// 1.创建 NSBlockOperation 对象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:
// 2.添加额外的操作
[op addExecutionBlock:
[op addExecutionBlock:
[op addExecutionBlock:
[op addExecutionBlock:
// 3.调用 start 方法开始执行操作
[op start];

NSOperation 自定义

1
2
自定义 NSOperation 的方式暂时不考虑
@interface YSCOperation : NSOperation

NSOperationQueue 操作队列

创建 Queue

1
2
3
4
5
6
// 获取主队列
[NSOperationQueue mainQueue];
// 获取当前队列
[NSOperationQueue currentQueue];
// 创建自定义队列 (可以通过其他属性来确定是串行或者并发 maxConcurrentOperationCount)
[[NSOperationQueue alloc] init];

向 NSOperationQueue 中添加操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 在 Queue 直接添加操作  不需要调用 NSOperation 的 start 方法 
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

// 2.创建操作
// 使用 NSInvocationOperation 创建操作1
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self

NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
[op3 addExecutionBlock:

// 3.使用 addOperation: 添加所有操作到队列中
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];

向 Queue 中直接加 Block 的方式

1
2
3
4
5
6
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.使用 addOperationWithBlock: 添加操作到队列中
[queue addOperationWithBlock:
[queue addOperationWithBlock:
[queue addOperationWithBlock:

控制并发和串行 maxConcurrentOperationCount

1
2
3
4
// 默认 -1 不控制
// 1 串行
// n 并发,但不会超过系统限制
@property NSInteger maxConcurrentOperationCount;

NSOperation 的依耐和优先级

依耐

NSOperation 的一个特点就是可以添加各种操作依赖,同时还可以设置一下参数,取消,暂停,恢复等。

1
2
3
4
// 依赖确定了谁在前执行和谁在后执行。(a的执行需要b执行完毕,a依耐b)
- (void)addDependency:(NSOperation *)op;
- (void)removeDependency:(NSOperation *)op;
@property (readonly, copy) NSArray<NSOperation *> *dependencies;

优先级

1
2
// 都处于就绪的状态的任务(操作),谁先开始执行,谁先取到。
@property NSOperationQueuePriority queuePriority;

线程间的通信

1
2
3
4
5
6
7
8
9
10
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];

// 2.添加操作
[queue addOperationWithBlock:^{
// 异步进行耗时操作
// 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
}];
}];

常用属性和方法归纳

NSOperation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 取消操作方法
- (void)cancel; 可取消操作,实质是标记 isCancelled 状态。

// 判断操作状态方法
- (BOOL)isFinished; 判断操作是否已经结束。
- (BOOL)isCancelled; 判断操作是否已经标记为取消。
- (BOOL)isExecuting; 判断操作是否正在在运行。
- (BOOL)isReady; 判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关。
操作同步
- (void)waitUntilFinished; 阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
- (void)setCompletionBlock:(void (^)(void))block; completionBlock 会在当前操作执行完毕时执行 completionBlock。
- (void)addDependency:(NSOperation *)op; 添加依赖,使当前操作依赖于操作 op 的完成。
- (void)removeDependency:(NSOperation *)op; 移除依赖,取消当前操作对操作 op 的依赖。
@property (readonly, copy) NSArray<NSOperation *> *dependencies; 在当前操作开始执行之前完成执行的所有操作对象数组。

NSOperationQueue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
取消/暂停/恢复操作
- (void)cancelAllOperations; 可以取消队列的所有操作。
- (BOOL)isSuspended; 判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。
- (void)setSuspended:(BOOL)b; 可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。
操作同步
- (void)waitUntilAllOperationsAreFinished; 阻塞当前线程,直到队列中的操作全部执行完毕。
添加/获取操作`
- (void)addOperationWithBlock:(void (^)(void))block; 向队列中添加一个 NSBlockOperation 类型操作对象。
- (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
- (NSArray *)operations; 当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。
- (NSUInteger)operationCount; 当前队列中的操作数。
获取队列
+ (id)currentQueue; 获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil
+ (id)mainQueue; 获取主队列。
- END -
扫一扫上面的二维码图案,加我微信

文章作者:梁大红

特别声明:若无特殊声明均为原创,转载请注明,侵权请联系

版权声明:署名-非商业性使用-禁止演绎 4.0 国际