线程同步方法和问题以及解决方案

在日常开发中,需要当几个任务执行完之后再执行最后一个任务。一下就列举几个线程同步的常用方法,并指出其中的问题,以及提出解决方按。

Dispatch Group 调度组

dispatch_group_notify 正常情况

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
- (void)dispathcGroupNotify {
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();

dispatch_queue_t queue = dispatch_queue_create("com.jiwuchao.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue1 = dispatch_queue_create("com.jiwuchao.www1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("com.jiwuchao.www2", DISPATCH_QUEUE_CONCURRENT);

dispatch_group_async(group, queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
}
});

dispatch_group_async(group, queue1, ^{

for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
}
});

dispatch_group_async(group, queue2, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3---%@",[NSThread currentThread]);
}
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"4---%@",[NSThread currentThread]);
}
NSLog(@"group---end");
});
}

输出结果

currentThread---<NSThread: 0x600002cc7c40>{number = 1, name = main}
group---begin
1---<NSThread: 0x600002ca3a00>{number = 4, name = (null)}
1---<NSThread: 0x600002c84300>{number = 3, name = (null)}
3---<NSThread: 0x600002c66840>{number = 8, name = (null)}
1---<NSThread: 0x600002c84300>{number = 3, name = (null)}
3---<NSThread: 0x600002c66840>{number = 8, name = (null)}
1---<NSThread: 0x600002ca3a00>{number = 4, name = (null)}
4---<NSThread: 0x600002cc7c40>{number = 1, name = main}
4---<NSThread: 0x600002cc7c40>{number = 1, name = main}
group---end

  以上执行结果是当任务1,2,3 执行完成之后才执行任务4 符合预期

dispatch_group_notify 异常情况

问题描述:

理想是 任务1 任务2 任务3 执行完成之后再执行任务4 为什有时会出现某个任务还没有执行完就开始执行任务4?比如有多个接口进行网络请求,当所有接口返回之后才刷新界面

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
- (void)dispathcGroupQuestion {
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();

dispatch_queue_t queue = dispatch_queue_create("com.jiwuchao.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue1 = dispatch_queue_create("com.jiwuchao.www1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("com.jiwuchao.www2", DISPATCH_QUEUE_CONCURRENT);
//任务1
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务1---%@",[NSThread currentThread]);
}
});
// 任务2
dispatch_group_async(group, queue1, ^{

// for (int i = 0; i < 2; ++i) {
// [NSThread sleepForTimeInterval:2];
// NSLog(@"任务2---%@",[NSThread currentThread]);
// }

dispatch_queue_t queue4 = dispatch_queue_create("wuchao.w", DISPATCH_QUEUE_CONCURRENT);
/*
如果dispatch_async 换成 dispatch_sync 则不会出现 上面的乱序问题y,因为并发队列 + 同步执行 并不能创建多个线程 所有任务2
的线程结束就是任务2及其子线程执行完的时机
*/
dispatch_async(queue4, ^{
// 子任务1
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务2的-子任务1 - %@",[NSThread currentThread]);
}
// 子任务2
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务2的-子任务2 - %@",[NSThread currentThread]);
}
// 子任务3
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务2的-子任务3 - %@",[NSThread currentThread]);
}
});

});
//任务3
dispatch_group_async(group, queue2, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务3---%@",[NSThread currentThread]);
}
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
NSLog(@"前面的异步操作都执行完毕");
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"开始执行dispatch_group_notify ---%@",[NSThread currentThread]);
}
NSLog(@"group---end");
});
}

输出:

currentThread---<NSThread: 0x600003787ec0>{number = 1, name = main}
group---begin
任务3---<NSThread: 0x60000372ed00>{number = 8, name = (null)}
任务1---<NSThread: 0x6000037eaa80>{number = 4, name = (null)}
任务2的-子任务1 - <NSThread: 0x6000037ca500>{number = 3, name = (null)}
任务3---<NSThread: 0x60000372ed00>{number = 8, name = (null)}
任务1---<NSThread: 0x6000037eaa80>{number = 4, name = (null)}
任务2的-子任务1 - <NSThread: 0x6000037ca500>{number = 3, name = (null)}
前面的异步操作都执行完毕
开始执行dispatch_group_notify ---<NSThread: 0x600003787ec0>{number = 1, name =
任务2的-子任务2 - <NSThread: 0x6000037ca500>{number = 3, name = (null)}
开始执行dispatch_group_notify ---<NSThread: 0x600003787ec0>{number = 1, name =
group---end
任务2的-子任务2 - <NSThread: 0x6000037ca500>{number = 3, name = (null)}
任务2的-子任务3 - <NSThread: 0x6000037ca500>{number = 3, name = (null)}
任务2的-子任务3 - <NSThread: 0x6000037ca500>{number = 3, name = (null)}

结论: 从上面的输出可以看出 ,当任务2 的子任务还没有执行完毕,就开始执行dispatch_group_notify中的任务,和预期任务2 执行完之后再执行 dispatch_group_notify中任务有出入

问题分析:为什么会出现这种情况?

原因是:因为对于dispatch_group_notify 来说判断其之前的任务执行完毕的==根据是任务2 的线程执行完毕,不考虑任务2中其他子任务的线程是否执行完毕==。此时任务2的子任务是异步并发执行,开启多个线程。并没有阻塞任务2的线程,所以在其他线程中执行任务2的子任务的时候任务2的线程已经执行完了。当任务1 任务2 任务3 执行完毕之后就开始执行dispatch_group_notify 中的任务。但是任务2 中的子任务还在执行,这时就看到了输出的那样的结果。

问题解决:方法 1,2

解决方法1 dispatch_group_enter 和 dispatch_group_leave

  dispatch_group_enter与dispatch_group_leave 组合比dispatch_group_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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
- (void)dispathcGroupAnswer {

NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();

dispatch_queue_t queue = dispatch_queue_create("com.jiwuchao.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue1 = dispatch_queue_create("com.jiwuchao.www1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("com.jiwuchao.www2", DISPATCH_QUEUE_CONCURRENT);

dispatch_group_enter(group);
//任务1
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务1---%@",[NSThread currentThread]);
}
dispatch_group_leave(group);
});
//任务2
dispatch_group_enter(group);
dispatch_async(queue1, ^{

dispatch_queue_t queue4 = dispatch_queue_create("wuchao.w", DISPATCH_QUEUE_CONCURRENT);
/*
如果dispatch_async 换成 dispatch_sync 则不会出现 上面的乱序问题y,因为并发队列 + 同步执行 并不能创建多个线程 所有任务2
的线程结束就是任务2及其子线程执行完的时机
*/
dispatch_async(queue4, ^{
// 子任务1
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务2的-子任务1 - %@",[NSThread currentThread]);
}
// 子任务2
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务2的-子任务2 - %@",[NSThread currentThread]);
}
// 子任务3
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务2的-子任务3 - %@",[NSThread currentThread]);
}
// 子任务1,2,3 是在同一个线程执行 当子任务1,2,3 执行完之后再leave 上层group 告诉nofify 这个任务2 已经完成了 才能正确完成同步 假如子任务中还有嵌套 那么还需要此种解决方法依次类推
dispatch_group_leave(group);
});
// 这一句放在这里是不行的
// dispatch_group_leave(group);
});
//任务3
dispatch_group_enter(group);
dispatch_async(queue2, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务3---%@",[NSThread currentThread]);
}
dispatch_group_leave(group);
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
NSLog(@"前面的异步操作都执行完毕");
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"开始执行dispatch_group_notify ---%@",[NSThread currentThread]);
}
NSLog(@"group---end");
});
NSLog(@"不会阻塞主线程-----");
}

输出:

currentThread---<NSThread: 0x60000094d380>{number = 1, name = main}
group---begin
不会阻塞主线程-----
任务1---<NSThread: 0x60000091aec0>{number = 4, name = (null)}
任务2的-子任务1 - <NSThread: 0x6000009e7a80>{number = 7, name = (null)}
任务3---<NSThread: 0x600000928300>{number = 8, name = (null)}
任务1---<NSThread: 0x60000091aec0>{number = 4, name = (null)}
任务2的-子任务1 - <NSThread: 0x6000009e7a80>{number = 7, name = (null)}
任务3---<NSThread: 0x600000928300>{number = 8, name = (null)}
任务2的-子任务2 - <NSThread: 0x6000009e7a80>{number = 7, name = (null)}
任务2的-子任务2 - <NSThread: 0x6000009e7a80>{number = 7, name = (null)}
任务2的-子任务3 - <NSThread: 0x6000009e7a80>{number = 7, name = (null)}
任务2的-子任务3 - <NSThread: 0x6000009e7a80>{number = 7, name = (null)}
前面的异步操作都执行完毕
开始执行dispatch_group_notify ---<NSThread: 0x60000094d380>{number = 1, name =
开始执行dispatch_group_notify ---<NSThread: 0x60000094d380>{number = 1, name =
group---end

结论:从输出接果可以看到输出结果符合预期。综合上述原因,用 dispatch_group_leave(group) 当任务2 的子任务的线程执行完之后才标识任务2执行完,才会触发 dispatch_group_notify 而不是当任务2线程执行完就触发dispatch_group_notify。

解决方法2 dispatch_semaphore_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
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
- (void)dispathcGroupAnswer2 {
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();

dispatch_queue_t queue = dispatch_queue_create("com.jiwuchao.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue1 = dispatch_queue_create("com.jiwuchao.www1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("com.jiwuchao.www2", DISPATCH_QUEUE_CONCURRENT);
//任务1
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务1---%@",[NSThread currentThread]);
}
});
// 任务2
dispatch_group_async(group, queue1, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue4 = dispatch_queue_create("wuchao.w", DISPATCH_QUEUE_CONCURRENT);
/*
如果dispatch_async 换成 dispatch_sync 则不会出现 上面的乱序问题y,因为并发队列 + 同步执行 并不能创建多个线程 所有任务2
的线程结束就是任务2及其子线程执行完的时机
*/

dispatch_async(queue4, ^{
// 子任务1
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务2的-子任务1 - %@",[NSThread currentThread]);
}
// 子任务2
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务2的-子任务2 - %@",[NSThread currentThread]);
}
// 子任务3
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务2的-子任务3 - %@",[NSThread currentThread]);
}


// 信号量加 1
dispatch_semaphore_signal(semaphore);

});

// 信号量 -1
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(15 * NSEC_PER_SEC)));
});
//任务3
dispatch_group_async(group, queue2, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"任务3---%@",[NSThread currentThread]);
}
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
NSLog(@"前面的异步操作都执行完毕");
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"开始执行dispatch_group_notify ---%@",[NSThread currentThread]);
}
NSLog(@"group---end");
});
}

打印


currentThread---<NSThread: 0x600002332a00>{number = 1, name = main}
group---begin
任务3---<NSThread: 0x600002343580>{number = 8, name = (null)}
任务1---<NSThread: 0x6000023435c0>{number = 9, name = (null)}
任务2的-子任务1 - <NSThread: 0x6000023591c0>{number = 4, name = (null)}
任务3---<NSThread: 0x600002343580>{number = 8, name = (null)}
任务2的-子任务1 - <NSThread: 0x6000023591c0>{number = 4, name = (null)}
任务1---<NSThread: 0x6000023435c0>{number = 9, name = (null)}
任务2的-子任务2 - <NSThread: 0x6000023591c0>{number = 4, name = (null)}
任务2的-子任务2 - <NSThread: 0x6000023591c0>{number = 4, name = (null)}
任务2的-子任务3 - <NSThread: 0x6000023591c0>{number = 4, name = (null)}
任务2的-子任务3 - <NSThread: 0x6000023591c0>{number = 4, name = (null)}
前面的异步操作都执行完毕
开始执行dispatch_group_notify ---<NSThread: 0x600002332a00>{number = 1, name = m
开始执行dispatch_group_notify ---<NSThread: 0x600002332a00>{number = 1, name = m
group---end

结论:

 输出结果符合预期。和dispatch_group_enter 和 dispatch_group_leave 作用一样。因为信号量初始化为0 ,当任务2 的子线程执行完成之后会执行

1
2
//信号量加  1
dispatch_semaphore_signal(semaphore);

使信号量加一操作,那么当走到

1
2
3
信号量加1 此方法的作用是,可以使总信号量减1,当信号总量为0时,再减一就小于0了,就会一直等待(阻塞所在线程),否则就可以正常执行。

dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(15 * NSEC_PER_SEC)));

这一句时,因为此时信号量等于1 ,那么不会阻塞当前(任务2)线程,那么任务2算是完成。假如任务2的子任务没有完成就不会走到 dispatch_semaphore_signal(semaphore) 这个方法,信号量仍然为0,会一直阻塞注任务2的线程。

dispatch_barrier_async 和 dispatch_barrier_sync 栅栏函数

dispatch_barrier_async

dispatch_barrier_async的作用是等待队列的前面的任务执行完毕后,才执行dispatch_barrier_async的block里面的任务,不会阻塞当前线程;

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
- (void)barrierAsync {
// 创建一个并发队列
dispatch_queue_t queue = dispatch_queue_create("com.jiwuchao.barrierAsync", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"1 - %@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"2 - %@",[NSThread currentThread]);
}
});

dispatch_barrier_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"i %ld dispatch_barrier_async 任务",i);
}
[NSThread sleepForTimeInterval:5];
});

dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"3 - %@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"4 - %@",[NSThread currentThread]);
}
});
NSLog(@"当前线程阻塞住了吗?没有");
}

打印:

2 - <NSThread: 0x600002ac0ec0>{number = 4, name = (null)}
1 - <NSThread: 0x600002a242c0>{number = 5, name = (null)}
1 - <NSThread: 0x600002a242c0>{number = 5, name = (null)}
2 - <NSThread: 0x600002ac0ec0>{number = 4, name = (null)}
i 0 dispatch_barrier_async 任务
i 1 dispatch_barrier_async 任务
3 - <NSThread: 0x600002ac0ec0>{number = 4, name = (null)}
4 - <NSThread: 0x600002ae3ec0>{number = 3, name = (null)}
4 - <NSThread: 0x600002ae3ec0>{number = 3, name = (null)}
3 - <NSThread: 0x600002ac0ec0>{number = 4, name = (null)}

结论 :

  1. dispatch_barrier_async 不会阻塞当前线程
  2. dispatch_barrier_async 会等到前面的任务执行完之后再执行后面的任务

dispatch_barrier_sync

dispatch_barrier_sync的作用是等待队列的前面的任务执行完毕后,才执行dispatch_barrier_async的block里面的任务,阻塞当前线程

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
- (void)barrierSync {
// 创建一个并发队列
dispatch_queue_t queue = dispatch_queue_create("com.jiwuchao.barrierAsync", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"1 - %@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"2 - %@",[NSThread currentThread]);
}
});

dispatch_barrier_sync(queue, ^{

for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"i %ld dispatch_barrier_sync 任务",i);
}
[NSThread sleepForTimeInterval:5];
});

dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"3 - %@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"4 - %@",[NSThread currentThread]);
}
});
NSLog(@"当前线程阻塞住了吗?阻塞住了");
}


打印:

2 - <NSThread: 0x60000346c4c0>{number = 5, name = (null)}
1 - <NSThread: 0x60000349f880>{number = 4, name = (null)}
1 - <NSThread: 0x60000349f880>{number = 4, name = (null)}
2 - <NSThread: 0x60000346c4c0>{number = 5, name = (null)}
i 0 dispatch_barrier_sync 任务
i 1 dispatch_barrier_sync 任务
当前线程阻塞住了吗?阻塞住了
4 - <NSThread: 0x60000345db80>{number = 10, name = (null)}
3 - <NSThread: 0x60000345dc00>{number = 9, name = (null)}
3 - <NSThread: 0x60000345dc00>{number = 9, name = (null)}
4 - <NSThread: 0x60000345db80>{number = 10, name = (null)}

当前线程阻塞住了吗?阻塞住了 “ 在当前线程中的输出 等到dispatch_barrier_sync 执行之后才执行

结论:

  1. dispatch_barrier_sync 会阻塞当前线程(主线程/非主线程)
  2. dispatch_barrier_sync会等到前面的任务执行完之后再执行后面的任务

问题描述:为什么没有等待 dispatch_barrier_async 之前的任务执行完就开始执行 dispatch_barrier_async 的block任务??

  上述情况会在dispatch_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
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
 
- (void)barrierAsynQuestion {
NSLog(@"1 current thread -%@",[NSThread currentThread]);
// 创建一个并发队列
dispatch_queue_t queue = dispatch_queue_create("com.jiwuchao.barrierAsync", DISPATCH_QUEUE_CONCURRENT);

//任务1
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"任务1 - %@",[NSThread currentThread]);
}
});
//任务2
dispatch_async(queue, ^{
NSLog(@"任务2 current thread -%@",[NSThread currentThread]);
/*
第一种情况 会开启新线程
*/
// // 1 和 dispatch_async(queue, ^{} 是不是同一个线程 重新开启一个线程 异步 执行完之后回调
// [Download downloadData:^(BOOL success) {
// NSLog(@"正在执行下载完成之后的任务");
// [NSThread sleepForTimeInterval:3];
// NSLog(@"下载完成之后的任务完成");
// }];

/*
第二种情况 如果是单个任务 不会开启新线程 如果是多个子任务则会开启线程
*/

dispatch_queue_t queue2 = dispatch_queue_create("com.wuchaoji.http", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue2, ^{
NSLog(@"3 current thread -%@",[NSThread currentThread]);
// 子任务1
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"子任务1 - %@",[NSThread currentThread]);
}
// 子任务2
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"子任务2 - %@",[NSThread currentThread]);
}
// 子任务3
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"子任务3 - %@",[NSThread currentThread]);
}
});

});

dispatch_barrier_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"i %ld dispatch_barrier_async 任务",i);
}
[NSThread sleepForTimeInterval:5];
});
// 任务3
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"任务3 - %@",[NSThread currentThread]);
}
});
// 任务4
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"任务4 - %@",[NSThread currentThread]);
}
});

}

打印:

1 current thread -<NSThread: 0x6000036be740>{number = 1, name = main}
任务2 current thread -<NSThread: 0x6000036d1000>{number = 4, name = (null)}
3 current thread -<NSThread: 0x600003633880>{number = 6, name = (null)}
任务1 - <NSThread: 0x600003631280>{number = 5, name = (null)}
子任务1 - <NSThread: 0x600003633880>{number = 6, name = (null)}
子任务1 - <NSThread: 0x600003633880>{number = 6, name = (null)}
任务1 - <NSThread: 0x600003631280>{number = 5, name = (null)}
子任务2 - <NSThread: 0x600003633880>{number = 6, name = (null)}
i 0 dispatch_barrier_async 任务
i 1 dispatch_barrier_async 任务
子任务2 - <NSThread: 0x600003633880>{number = 6, name = (null)}
子任务3 - <NSThread: 0x600003633880>{number = 6, name = (null)}
子任务3 - <NSThread: 0x600003633880>{number = 6, name = (null)}
任务3 - <NSThread: 0x6000036e9380>{number = 3, name = (null)}
任务4 - <NSThread: 0x600003633880>{number = 6, name = (null)}
任务3 - <NSThread: 0x6000036e9380>{number = 3, name = (null)}
任务4 - <NSThread: 0x600003633880>{number = 6, name = (null)}

问题分析:和group_notify 类似

情况1 在嵌套任务中有一个任务:

在并发队列中有单个任务时不会开启新的线程,那么和当前线程(任务2)的线程是同一个线程,当前任务2执行完也就是其中子线程的任务执行完毕。

情况2 在嵌套任务中有多个任务:

在并发队列中有多个任务就会开启多个线程异步执行,多个任务异步执行并不会阻塞任务2 的线程,那么任务2 线程执行完毕的标准是把其子任务全部提交到执行队列,那么此时dispatch_barrier_async 检测到 任务1 任务2 都已经完成就会执行dispatch_barrier_async 的任务,但是任务2 系统对完成的判断标准是把其子任务提交到执行队列,此线程的任务已经完成,但是此线程其他的线程它不负责
所以会出现在看似 线程2还没有执行完就开始执行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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

- (void)barrierAsynAnswer {
NSLog(@"1 current thread -%@",[NSThread currentThread]);
// 创建一个并发队列
dispatch_queue_t queue = dispatch_queue_create("com.jiwuchao.barrierAsync", DISPATCH_QUEUE_CONCURRENT);

//任务1
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"任务1 - %@",[NSThread currentThread]);
}
});
//任务2
dispatch_async(queue, ^{
NSLog(@"任务2 current thread -%@",[NSThread currentThread]);
/*
第一种情况 会开启新线程
*/
// // 1 和 dispatch_async(queue, ^{} 是不是同一个线程 重新开启一个线程 异步 执行完之后回调
// [Download downloadData:^(BOOL success) {
// NSLog(@"正在执行下载完成之后的任务");
// [NSThread sleepForTimeInterval:3];
// NSLog(@"下载完成之后的任务完成");
// }];

/*
第二种情况 如果是单个任务 不会开启新线程 如果是多个子任务则会开启线程
*/

dispatch_queue_t queue2 = dispatch_queue_create("com.wuchaoji.http", DISPATCH_QUEUE_CONCURRENT);

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

dispatch_async(queue2, ^{
NSLog(@"3 current thread -%@",[NSThread currentThread]);
// 子任务1
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"子任务1 - %@",[NSThread currentThread]);
}
// 子任务2
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"子任务2 - %@",[NSThread currentThread]);
}
// 子任务3
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"子任务3 - %@",[NSThread currentThread]);
}

dispatch_semaphore_signal(semaphore);
});
// 信号量 等待 最多等待15秒
dispatch_semaphore_wait(semaphore,dispatch_time(DISPATCH_TIME_NOW, (int64_t)(15 * NSEC_PER_SEC)));

});

dispatch_barrier_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"i %ld dispatch_barrier_async 任务",i);
}
[NSThread sleepForTimeInterval:5];
});
// 任务3
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"任务3 - %@",[NSThread currentThread]);
}
});
// 任务4
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:1];
NSLog(@"任务4 - %@",[NSThread currentThread]);
}
});

}

打印:

1 current thread -<NSThread: 0x600001327680>{number = 1, name = main}
任务2 current thread -<NSThread: 0x60000136ec80>{number = 3, name = (null)}
3 current thread -<NSThread: 0x6000013b1b80>{number = 5, name = (null)}
子任务1 - <NSThread: 0x6000013b1b80>{number = 5, name = (null)}
任务1 - <NSThread: 0x60000134f500>{number = 4, name = (null)}
子任务1 - <NSThread: 0x6000013b1b80>{number = 5, name = (null)}
任务1 - <NSThread: 0x60000134f500>{number = 4, name = (null)}
子任务2 - <NSThread: 0x6000013b1b80>{number = 5, name = (null)}
子任务2 - <NSThread: 0x6000013b1b80>{number = 5, name = (null)}
子任务3 - <NSThread: 0x6000013b1b80>{number = 5, name = (null)}
子任务3 - <NSThread: 0x6000013b1b80>{number = 5, name = (null)}
i 0 dispatch_barrier_async 任务
i 1 dispatch_barrier_async 任务
任务3 - <NSThread: 0x60000136ec80>{number = 3, name = (null)}
任务4 - <NSThread: 0x6000013819c0>{number = 6, name = (null)}
任务4 - <NSThread: 0x6000013819c0>{number = 6, name = (null)}
任务3 - <NSThread: 0x60000136ec80>{number = 3, name = (null)}

结论:
 输出结果符合预期。因为:在任务2 开始执行的时候生成一个信号量,信号量为0 dispatch_semaphore_wait 会一直阻塞任务2线程,直到超出等待时间。直到任务2 的子任务执行完之后才dispatch_semaphore_signal 使信号量加一 这样dispatch_semaphore_wait 取消阻塞任务2线程

Dispatch Semaphore 信号量

 GCD 中的信号量是指 Dispatch Semaphore,是持有计数的信号。类似于过高速路收费站的栏杆。可以通过时,打开栏杆,不可以通过时,关闭栏杆。在 Dispatch Semaphore 中,使用计数来完成这个功能,计数为0时等待,不可通过。计数为1或大于1时,计数减1且不等待,可通过。
Dispatch Semaphore 提供了三个函数。

  1. dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量
  2. dispatch_semaphore_signal:发送一个信号,让信号总量加1
  3. dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。

注意:信号量的使用前提是:想清楚你需要处理哪个线程等待(阻塞),又要哪个线程继续执行,然后使用信号量

Dispatch Semaphore 在实际开发中主要用于:

  1. 保持线程同步,将异步执行任务转换为同步执行任务
  2. 保证线程安全,为线程加锁

在此主要说下线程同步。

  有一个全局的变量 number 要求在其他线程异步执行完赋值操作之后再输出,输出的值是在其他线程所赋的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)synchronization {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"semaphore---begin");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

__block int number = 0;
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
number = 100;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end,number = %d",number);
}

打印:

currentThread---<NSThread: 0x600000d9a800>{number = 1, name = main}
semaphore---begin
1---<NSThread: 0x600000d17f40>{number = 6, name = (null)}
semaphore---end,number = 100

结论: 在其他线程异步赋值的number 输出的值正确,如果没有用dispatch_semaphore 那么输出的值是 number的初始化的值 0. 这里dispatch_semaphore 的目的是让执行synchronization 方法的main 线程和执行为number赋值的子线程 保持同步。

线程同步总结

线程同步 Dispatch Group 中dispatch_group_enter 和 dispatch_group_leave 以及dispatch_group_async 比较

Dispatch Group 中有 dispatch_group_enter 和 dispatch_group_leave 以及dispatch_group_async
。dispatch_group_async
使用比较方便,但是不够灵活,稍微复杂一点的需求处理不是太容易。
dispatch_group_enter 和 dispatch_group_leave
使用没有dispatch_group_async
简单,但是比较灵活,如上面解决复杂的同步问题。

dispatch_semaphore 和 dispatch group

dispatch_semaphore 使用灵活,但是处理比较复杂的常见不太方便。最适合的是dispatch_semaphore 和 dispatch group 配合使用,才能灵活处理复杂场景。

结尾

  线程同步,需要根据不同的场景选择不同的技术,同时也要注意使用过程中的问题。

附Demo

相关Demo这里点击 https://github.com/JiWuChao/OCKeyPoint/tree/master/Multithreading/GCD

坚持原创,您的支持将鼓励我继续创作!