Hello, World!

iOS并发编程进阶

字数统计: 5k阅读时长: 24 min
2018/01/16 Share

NSThread

需求一:用NSThread实现三个线程任务A、B、C,它们结束之后,再执行D、E任务。
思路:用KVO监听NSThread实例的isFinished属性。

1
demo

需求二:把有三个参数的函数放在子线程执行。

1
NSThread暂时实现不了。

NSOperation

需求一:用NSOperation实现三个线程任务A、B、C,它们结束之后,再执行D、E任务。

  • 方式一:先执行封装了A + B + C操作的NSBlockOperation,KVO监听属性finished=YES时,再执行封装了D + E操作的NSBlockOperation对象。
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
// NSBlockOperation
- (void)demo {
// 任务
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{

[NSThread sleepForTimeInterval:1.f];
NSLog(@"%@ work A done.", [NSThread currentThread]);
}];

void(^task)(void) = ^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"%@ work B done.", [NSThread currentThread]);
};
[blockOperation addExecutionBlock:task];

[blockOperation addExecutionBlock:^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"%@ work C done.", [NSThread currentThread]);
}];

// 监听NSOperation状态
[blockOperation addObserver:self forKeyPath:@"finished" options:NSKeyValueObservingOptionNew context:nil];

// 队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:blockOperation];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"finished"] && [change valueForKey:NSKeyValueChangeNewKey]) {
[self performMehtod];
}
}

- (void)performMehtod {

NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"%@ work D done.", [NSThread currentThread]);
}];

[blockOperation addExecutionBlock:^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"%@ work E done.", [NSThread currentThread]);
}];

[blockOperation start];
}

log信息显示如下:

1
2
3
4
5
<NSThread: 0x600001944240>{number = 3, name = (null)} work A done.
<NSThread: 0x600001960100>{number = 8, name = (null)} work B done.
<NSThread: 0x600001966180>{number = 9, name = (null)} work C done.
<NSThread: 0x600001944240>{number = 3, name = (null)} work D done.
<NSThread: 0x600001960100>{number = 8, name = (null)} work E done.
  • 方式二:使用操作依赖功能,依赖的底层实现就是基于KVO的。
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
// NSBlockOperation
- (void)demo {
// 任务
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{

[NSThread sleepForTimeInterval:1.f];
NSLog(@"%@ work A done.", [NSThread currentThread]);
}];

void(^task)(void) = ^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"%@ work B done.", [NSThread currentThread]);
};
[blockOperation addExecutionBlock:task];

[blockOperation addExecutionBlock:^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"%@ work C done.", [NSThread currentThread]);
}];

NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"%@ work D done.", [NSThread currentThread]);
}];

[blockOperation1 addExecutionBlock:^{
[NSThread sleepForTimeInterval:1.f];
NSLog(@"%@ work E done.", [NSThread currentThread]);
}];

// 通过操作依赖可以实现先后顺序
[blockOperation1 addDependency:blockOperation];

// 队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:blockOperation];
[queue addOperation:blockOperation1];
}

log信息显示如下:

1
2
3
4
5
<NSThread: 0x600000717640>{number = 5, name = (null)} work C done.
<NSThread: 0x600000760700>{number = 6, name = (null)} work A done.
<NSThread: 0x600000776e80>{number = 8, name = (null)} work B done.
<NSThread: 0x600000776e80>{number = 8, name = (null)} work D done.
<NSThread: 0x600000717640>{number = 5, name = (null)} work E done.

需求二:把有三个参数的函数放在子线程执行。

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// NSInvocationOperation
@interface DemoViewController ()
@property (nonatomic, strong) NSInvocation *invocation;
@end

@implementation DemoViewController

- (void)viewDidLoad {
[super viewDidLoad];

[self demo];
}

// NSInvocation + NSMethodSignature 功能:方法的对象化,包括相关的结构信息:返回值,调用者,方法名,参数
- (void)demo {
// 方法签名
NSMethodSignature *signature = [self methodSignatureForSelector:@selector(name:age:sex:)];

// NSInvocation作用是把方法对象化
self.invocation = [NSInvocation invocationWithMethodSignature:signature];
/**
<NSInvocation: 0x604000471ac0>
return value: {@} 0x0
target: {@} 0x0
selector: {:} null
argument 2: {@} 0x0
argument 3: {@} 0x0
argument 4: {@} 0x0
*/
self.invocation.target = self;
self.invocation.selector = @selector(name:age:sex:);
// 和签名的seletor要对应起来
// 配置参数
NSString *name = @"kobe";
NSString *age = @"40";
NSString *sex = @"man";
[self.invocation setArgument:&name atIndex:2];
[self.invocation setArgument:&age atIndex:3];
[self.invocation setArgument:&sex atIndex:4];
// [self.invocation invoke]; // 调用方法

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithInvocation:self.invocation];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];

[operation addObserver:self forKeyPath:@"finished" options:NSKeyValueObservingOptionNew context:nil];
}

- (NSString *)name:(NSString *)name age:(NSString *)age sex:(NSString *)sex {
NSLog(@"name: age: sex:%@", [NSThread currentThread]);
return [NSString stringWithFormat:@"%@-%@-%@", name, age, sex];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"finished"] && [change valueForKey:NSKeyValueChangeNewKey]) {
__unsafe_unretained NSString *returnValue;
[self.invocation getReturnValue:&returnValue];
NSLog(@"returnValue:%@", returnValue);
}
}

@end

log信息显示如下:

1
2
name: age: sex:<NSThread: 0x600001b8cd00>{number = 6, name = (null)}
returnValue:kobe-40-man

自定义NSOperation

1
2
3
4
5
#import <Foundation/Foundation.h>

@interface NSCustomOperation : NSOperation

@end
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
#import "NSCustomOperation.h"

@implementation NSCustomOperation

@synthesize finished = _finished;

// 主要的业务逻辑放到main处理
- (void)main {
if (!self.isCancelled) {
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"---%@---", [NSThread currentThread]);
}
}
_finished = YES;
}

- (void)start {
// 异常处理
if (self.isFinished) {
return;
}
if (self.isExecuting) {
return;
}

[self main];
}

// NSOperation对象销毁前,finished属性必须为YES,否则不销毁。
// 如果 dealloc 没被执行,是因为 _finished = NO
- (void)dealloc {
NSLog(@"%s", __func__);
}

@end

操作依赖

可以使任务有序执行。操作依赖强大的是可以跨队列依赖。

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
- (void)method3 {
// 实例化4个操作
NSBlockOperation *operationOne = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1- %@",[NSThread currentThread]);
}];
NSBlockOperation *operationTwo = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2- %@",[NSThread currentThread]);
}];
NSBlockOperation *operationThree = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3- %@",[NSThread currentThread]);
}];
NSBlockOperation *operationFour = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"4- %@",[NSThread currentThread]);
}];

// 给操作添加依赖
[operationTwo addDependency:operationOne];
[operationThree addDependency:operationTwo];
[operationFour addDependency:operationThree];
// [operationOne addDependency: operationFour]; // 一定不能出现循环依赖

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operationOne];
[[NSOperationQueue mainQueue] addOperation:operationTwo];
[queue addOperation:operationThree];
[queue addOperation:operationFour];
}

log信息显示如下:

1
2
3
4
1- <NSThread: 0x600000931700>{number = 7, name = (null)}
2- <NSThread: 0x600000900240>{number = 1, name = main}
3- <NSThread: 0x600000948380>{number = 5, name = (null)}
4- <NSThread: 0x600000948380>{number = 5, name = (null)}

1-2-3-4,任务是有序执行的,而且操作依赖更强大的是可以跨队列设置,就算第2步的操作是添加在主队列中的,结果依然有效。

线程取消

正在运行的线程不能被真正的取消,只是标识状态节点的作用。

GCD

dispatch_group_t (队列组)

在组内的任务都执行完毕后,再去执行其他操作。简言之,可以获取到多个并发线程全部执行结束的时间点,汇总结果。
队列组内任务只支持异步执行。

  • 方式一:
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
- (void)demo {

NSLog(@"__BEGIN:%@__", [NSThread currentThread]);

dispatch_queue_t queue1 = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("com.abc", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue3 = dispatch_queue_create("com.abcd", DISPATCH_QUEUE_CONCURRENT);

dispatch_block_t block1 = ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"----执行第一个任务---当前线程%@",[NSThread currentThread]);
};

dispatch_block_t block2 = ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"----执行第二个任务---当前线程%@",[NSThread currentThread]);
};

dispatch_block_t block3 = ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"----执行第三个任务---当前线程%@",[NSThread currentThread]);
};

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue1, block1);
dispatch_group_async(group, queue2, block2);
dispatch_group_async(group, queue3, block3);
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"----执行最后的汇总任务---当前线程%@",[NSThread currentThread]);
});

NSLog(@"__END:%@__", [NSThread currentThread]);
}

log信息显示如下:

1
2
3
4
5
6
__BEGIN:<NSThread: 0x600003fa8280>{number = 1, name = main}__
__END:<NSThread: 0x600003fa8280>{number = 1, name = main}__
----执行第一个任务---当前线程<NSThread: 0x600003fc40c0>{number = 7, name = (null)}
----执行第二个任务---当前线程<NSThread: 0x600003fe8040>{number = 9, name = (null)}
----执行第三个任务---当前线程<NSThread: 0x600003fccc40>{number = 10, name = (null)}
----执行最后的汇总任务---当前线程<NSThread: 0x600003fa8280>{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
- (void)demo {
NSLog(@"__BEGIN:%@__", [NSThread currentThread]);

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_group_enter(group); // 组内任务数加一
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"----执行第一个任务---当前线程%@", [NSThread currentThread]);
dispatch_group_leave(group); // 组内任务数减一
});

dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"----执行第二个任务---当前线程%@", [NSThread currentThread]);
dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"----执行第三个任务---当前线程%@", [NSThread currentThread]);
dispatch_group_leave(group);
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"----执行最后的汇总任务---当前线程%@", [NSThread currentThread]);
});

NSLog(@"__END:%@__", [NSThread currentThread]);
}

log信息显示如下:

1
2
3
4
5
6
__BEGIN:<NSThread: 0x600002f14400>{number = 1, name = main}__
__END:<NSThread: 0x600002f14400>{number = 1, name = main}__
----执行第二个任务---当前线程<NSThread: 0x600002f7cc00>{number = 8, name = (null)}
----执行第一个任务---当前线程<NSThread: 0x600002f55540>{number = 3, name = (null)}
----执行第三个任务---当前线程<NSThread: 0x600002f78800>{number = 9, name = (null)}
----执行最后的汇总任务---当前线程<NSThread: 0x600002f14400>{number = 1, name = main}

dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数+1。dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数-1。当group中未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify中的任务。

和内存管理的引用计数类似,我们可以假设group也持有一个整形变量,当调用enter时计数加1,调用leave时计数减1,当计数为0时会调用dispatch_group_notify

以上两种基本使用方式注意事项:
添加到队列中的任务,必须是同步的,如果再开启新线程,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
38
39
40
41
42
43
44
45
46
- (void)demo {
NSLog(@"__BEGIN:%@__", [NSThread currentThread]);

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_group_enter(group); // 组内任务数加一
dispatch_async(queue, ^{
[self network];
dispatch_group_leave(group); // 组内任务数减一
});

dispatch_group_enter(group);
dispatch_async(queue, ^{
[self network];
dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_async(queue, ^{
[self network];
dispatch_group_leave(group);
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"----执行最后的汇总任务---当前线程%@", [NSThread currentThread]);
});

NSLog(@"__END:%@__", [NSThread currentThread]);
}

- (void) network {
NSString *urlstr = @"https://www.xxx.com";
NSURL *url = [NSURL URLWithString:urlstr];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
// 信号量:异步变同步。
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSDictionary *infoDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSLog(@"%@_%@",[NSThread currentThread], infoDict.class);
dispatch_semaphore_signal(sema);
}];
[task resume];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}

log信息显示如下:

1
2
3
4
5
6
__BEGIN:<NSThread: 0x6000036d8500>{number = 1, name = main}__
__END:<NSThread: 0x6000036d8500>{number = 1, name = main}__
<NSThread: 0x600003687b00>{number = 9, name = (null)}___NSDictionaryI
<NSThread: 0x600003687b00>{number = 9, name = (null)}___NSDictionaryI
<NSThread: 0x600003687a40>{number = 10, name = (null)}___NSDictionaryI
----执行最后的汇总任务---当前线程<NSThread: 0x6000036d8500>{number = 1, name = main}

dispatch_semaphore_t(信号量)

信号量,相当于NSOperationQueue中最大并发数的功能,用来控制线程的数量。
示例一:

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
// 信号量可以理解为最大并发数 具备异步变同步的功能
- (void)demo {
// create的value表示,最多几个资源可访问
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
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);
});
}

log信息显示如下:

1
2
3
4
5
6
run task 1
run task 2
complete task 2
complete task 1
run task 3
complete task 3

示例二:

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
- (void)demo {
NSLog(@"__BEGIN:%@__", [NSThread currentThread]);
// 创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"----开始执行第一个任务---当前线程%@",[NSThread currentThread]);

[NSThread sleepForTimeInterval:2];

NSLog(@"----结束执行第一个任务---当前线程%@",[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});

dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"----开始执行第二个任务---当前线程%@",[NSThread currentThread]);

[NSThread sleepForTimeInterval:2];

NSLog(@"----结束执行第二个任务---当前线程%@",[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});

dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"----开始执行第三个任务---当前线程%@",[NSThread currentThread]);

[NSThread sleepForTimeInterval:1];

NSLog(@"----结束执行第三个任务---当前线程%@",[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});

NSLog(@"__END:%@__", [NSThread currentThread]);
}

log信息显示如下:

1
2
3
4
5
6
7
8
__BEGIN:<NSThread: 0x600002d5c140>{number = 1, name = main}__
__END:<NSThread: 0x600002d5c140>{number = 1, name = main}__
----开始执行第二个任务---当前线程<NSThread: 0x600002d18980>{number = 4, name = (null)}
----结束执行第二个任务---当前线程<NSThread: 0x600002d18980>{number = 4, name = (null)}
----开始执行第一个任务---当前线程<NSThread: 0x600002d01580>{number = 7, name = (null)}
----结束执行第一个任务---当前线程<NSThread: 0x600002d01580>{number = 7, name = (null)}
----开始执行第三个任务---当前线程<NSThread: 0x600002d62300>{number = 6, name = (null)}
----结束执行第三个任务---当前线程<NSThread: 0x600002d62300>{number = 6, name = (null)}

dispatch_barrier_async(栅栏)

在并发队列中,为了保持任务的顺序,让一部分先执行完成后才能继续进行,可以使用dispatch_barrier_async来实现。
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
37
38
39
40
41
42
43
44
45
46
47
48
49
- (void)demo {

NSLog(@"__BEGIN:%@__", [NSThread currentThread]);

dispatch_queue_t queue = dispatch_queue_create("com.test.testQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:3];

NSLog(@"----执行第一个写入任务---当前线程%@",[NSThread currentThread]);

});

dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];

NSLog(@"----执行第二个写入任务---当前线程%@",[NSThread currentThread]);

});

dispatch_barrier_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"----等待前面的任务完成 一阶段---当前线程%@",[NSThread currentThread]);
});

dispatch_barrier_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"----等待前面的任务完成 二阶段---当前线程%@",[NSThread currentThread]);
});

dispatch_async(queue, ^{
// 第一个读取任务
[NSThread sleepForTimeInterval:1];

NSLog(@"----执行第一个读取任务---当前线程%@",[NSThread currentThread]);

});

dispatch_async(queue, ^{
// 第二个读取任务
[NSThread sleepForTimeInterval:3];

NSLog(@"----执行第二个读取任务---当前线程%@",[NSThread currentThread]);

});

NSLog(@"__END:%@__", [NSThread currentThread]);

}

log信息显示如下:

1
2
3
4
5
6
7
8
__BEGIN:<NSThread: 0x600001ac0400>{number = 1, name = main}__
__END:<NSThread: 0x600001ac0400>{number = 1, name = main}__
----执行第二个写入任务---当前线程<NSThread: 0x600001aa6c40>{number = 7, name = (null)}
----执行第一个写入任务---当前线程<NSThread: 0x600001aa6ec0>{number = 8, name = (null)}
----等待前面的任务完成 一阶段---当前线程<NSThread: 0x600001aa6ec0>{number = 8, name = (null)}
----等待前面的任务完成 二阶段---当前线程<NSThread: 0x600001aa6ec0>{number = 8, name = (null)}
----执行第一个读取任务---当前线程<NSThread: 0x600001aa6ec0>{number = 8, name = (null)}
----执行第二个读取任务---当前线程<NSThread: 0x600001aa6c40>{number = 7, name = (null)}

注意:dispatch_barrier_async一定要搭配自定义的队列使用,不能搭配系统提供的全局并发队列,否则dispatch_barrier_async作用和dispatch_async一样。

【dispatch_barrier_async 与 dispatch_barrier_sync异同】

共同点:
  等待在它前面插入队列的任务先执行完,等待他们自己的任务执行完再执行后面的任务
不同点:
  1、dispatch_barrier_sync将自己内部的任务插入到队列的之后,需要把该任务执行结束之后才会继续插入后面的任务。
  2、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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@interface BarrierViewController () {
NSMutableArray *_safeAry;
dispatch_queue_t _queue;
}
@end

@implementation BarrierViewController

- (void)viewDidLoad {
[super viewDidLoad];

_safeAry = [NSMutableArray array];
[_safeAry addObject:@"0"];
[_safeAry addObject:@"1"];
[_safeAry addObject:@"2"];
[_safeAry addObject:@"3"];
_queue = dispatch_queue_create("com.barrier1.concurrent", DISPATCH_QUEUE_CONCURRENT);

[self demo];
}

/**
多线程来操作可变数组mutableAry,肯定会出错,因为集合都是线程不安全的。
解决方案:使用dispatch_barrier_async。 只要涉及到写操作(要做保护)
*/
- (void) demo {

dispatch_queue_t queue = dispatch_queue_create("com.barrier2.concurrent", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 20; i++) {
// 写
dispatch_async(queue, ^{
[self addObject:[NSString stringWithFormat:@"%d", i + 4]];
});

// 读
dispatch_async(queue, ^{
NSLog(@"%d::%@", i, [self indexTo:i]);
});
}
}

// 写 保证只有一个在操作(避免了同时多个写操作导致的问题)
- (void)addObject:(NSString *)object {
dispatch_barrier_async(_queue, ^{
if (object != nil) {
[_safeAry addObject:object];
}
});
}

// 注意同步,因为业务关系,必须马上返回数据
- (NSString *)indexTo:(NSInteger)index {
__block NSString *result = nil;
dispatch_sync(_queue, ^{
if (index < _safeAry.count) {
result = _safeAry[index];
}
});
return result;
}

@end

dispatch_apply(重复)

dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API,将任务按照指定的次数追加到队列中,并等待全部队列任务执行结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)demo {
dispatch_queue_t queue = dispatch_queue_create("com.app.concurrent", DISPATCH_QUEUE_CONCURRENT);

/**
同步队列组
@param 5 指定次数
@param queue 追加对象的Dispatch Queue
@param count 带有参数的Block, count的作用是为了按执行的顺序区分各个Block
*/
dispatch_apply(5, queue, ^(size_t count) {
NSLog(@"%zu", count);
});

NSLog(@"Done!");
}

log信息显示如下:

1
2
3
4
5
6
0
1
2
3
4
Done!

如果在for循环中使用 dispatch_async 需要管理好线程的数量,否则会发生线程爆炸或死锁。而dispatch_apply是由GCD管理并发的,可以避免上述情况发生。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- (void)demo {
dispatch_queue_t queue = dispatch_queue_create("com.app.concurrent", DISPATCH_QUEUE_CONCURRENT);

// // 有问题的情况,可能会死锁
// for (int i = 0; i < 999 ; i++) {
// dispatch_async(queue, ^(size_t i){
// NSLog(@"wrong %zu", i);
// });
// }

// 正确方式
dispatch_apply(999, queue, ^(size_t i){
NSLog(@"correct %zu", i);
});

// 等待上述任务执行结束之后,当前线程才会继续往下执行。
NSLog(@"Done!");
}

dispatch_after(延后)

1
2
3
4
5
6
7
8
9
10
11
12
- (void)demo {

dispatch_queue_t queue = dispatch_queue_create("com.timer.concurrent", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
NSLog(@"start:%@", [NSThread currentThread]);
dispatch_after(1, queue, ^{
NSLog(@"dispatch_after:%@", [NSThread currentThread]);
});
NSLog(@"end");
});
}

log信息显示如下:

1
2
3
start:<NSThread: 0x6000020a1d80>{number = 7, name = (null)}
end
dispatch_after:<NSThread: 0x600002081380>{number = 8, name = (null)}

注意:dispatch_after原理是,先把操作添加到队列,指定时间后再执行,而不是指定时间后才把操作添加到队列。

dispatch_once

代码从程序启动就执行一次

1
2
3
4
5
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//要执行的代码块
//只会被执行一次
});

实现机制:在静态区分配内存给onceToken变量,并且初始化为0(静态区变量为全局变量,且直到程序退出才会释放onceToken),线程执行到这里时,判断onceToken的值为0,就去执行block,并且onceToken的值更改为-1,第二次再有线程来执行时,判断onceToken的值为-1,就不执行block。

苹果推荐单例的创建方式为:

1
2
3
4
5
6
7
8
+ (instancetype)sharedSingleton {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}

dispatch_queue_t激活

使用DISPATCH_QUEUE_CONCURRENT_INACTIVE创建的队列,是未激活队列,在使用的时候,需要手动激活。

1
2
3
4
5
6
7
- (void)demo {
dispatch_queue_t queue = dispatch_queue_create("com.active.concurrent", DISPATCH_QUEUE_CONCURRENT_INACTIVE);
dispatch_async(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
dispatch_activate(queue);
}

log信息显示如下:

1
<NSThread: 0x600000178080>{number = 3, name = (null)}

dispatch_source

用dispatch_source实现定时器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dispatch_source_t source;
- (void)timeSource {
// 1 创建一个队列
dispatch_queue_t queue = dispatch_queue_create("com.source.serial", DISPATCH_QUEUE_SERIAL);
// io source关联到队列
source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 配置soure的时间
dispatch_source_set_timer(source, DISPATCH_TIME_NOW, 1, 1);
// 配置source的处理事件
dispatch_source_set_event_handler(source, ^{
NSLog(@"soure_event:==%@", [NSThread currentThread]);
});
// 开启定时器
dispatch_resume(source);
}

dispatch_async_f

1
2
3
4
5
6
7
8
void testMethod() {
NSLog(@"testMethod::-->%@", [NSThread currentThread]);
}

- (void)demo {
dispatch_queue_t queue = dispatch_queue_create("com.f.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async_f(queue, nil, testMethod);
}

log信息显示如下:

1
testMethod::--><NSThread: 0x600003c8fa00>{number = 3, name = (null)}

僵死线程

先执行如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (void)viewDidLoad {
[super viewDidLoad];

[NSThread detachNewThreadSelector:@selector(ThreadOne) toTarget:self withObject:nil];
}

- (void)ThreadOne {
NSLog(@"start");

[self performSelector:@selector(method_one) withObject:nil afterDelay:2];

NSLog(@"end");
}

- (void)method_one {
NSLog(@"%s", __func__);
}

log信息显示如下:

1
2
start
end

根据log信息可知,method_one函数没有被执行。
原因:子线程执行完ThreadOne函数之后,任务就完成了,被回收了。不会再等待2秒钟之后去执行method_one函数了。
解决方式:开启runloop,保活线程。如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (void)viewDidLoad {
[super viewDidLoad];

[NSThread detachNewThreadSelector:@selector(ThreadOne) toTarget:self withObject:nil];
}

- (void)ThreadOne {
NSLog(@"start");

[self performSelector:@selector(method_one) withObject:nil afterDelay:2];

NSPort *port = [[NSPort alloc] init];
[[NSRunLoop currentRunLoop] addPort:port forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];

NSLog(@"end");
}

- (void)method_one {
NSLog(@"%s", __func__);
}

log信息显示如下:

1
2
start
-[ViewController method_one]

根据log信息可知,method_one函数被执行了。
但是有个新问题产生了,程序始终没有执行到NSLog(@”end”)代码,造成了内存泄露。
解决方式:在method_one函数中,关闭runloop。如下所示

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
@interface ViewController () {
NSPort *_port;
}

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

[NSThread detachNewThreadSelector:@selector(ThreadOne) toTarget:self withObject:nil];
}

- (void)ThreadOne {
NSLog(@"start");

[self performSelector:@selector(method_one) withObject:nil afterDelay:2];

_port = [[NSPort alloc] init];
[[NSRunLoop currentRunLoop] addPort:_port forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];

NSLog(@"end");
}

- (void)method_one {
[[NSRunLoop currentRunLoop] removePort:_port forMode:NSDefaultRunLoopMode];
NSLog(@"%s", __func__);
}

@end

log信息显示如下:

1
2
3
start
-[ViewController method_one]
end

在A线程中开启定时器,必须在A线程中关闭,否则会造成内存泄露。

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
@interface ViewController () {
NSTimer *_timer;
}

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

[NSThread detachNewThreadSelector:@selector(ThreadTwo) toTarget:self withObject:nil];
}

- (void)ThreadTwo {

NSLog(@"start");

_timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(stopTimer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];

NSLog(@"end");
}

- (void)stopTimer {

NSLog(@"stopTimer");
static int count = 0;
count++;
if (count > 5) {
[_timer invalidate];
}

}

@end

log信息显示如下:

1
2
3
4
5
6
7
8
start
stopTimer
stopTimer
stopTimer
stopTimer
stopTimer
stopTimer
end
CATALOG
  1. 1. NSThread
  2. 2. NSOperation
    1. 2.1. 自定义NSOperation
    2. 2.2. 操作依赖
    3. 2.3. 线程取消
  3. 3. GCD
    1. 3.1. dispatch_group_t (队列组)
    2. 3.2. dispatch_semaphore_t(信号量)
    3. 3.3. dispatch_barrier_async(栅栏)
    4. 3.4. dispatch_apply(重复)
    5. 3.5. dispatch_after(延后)
    6. 3.6. dispatch_once
    7. 3.7. dispatch_queue_t激活
    8. 3.8. dispatch_source
    9. 3.9. dispatch_async_f
  4. 4. 僵死线程