iOS多线程 - GCD基础实践

Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。下面介绍GCD的基础使用.

0x01 基本组合

两种队列+两种任务执行方式
异步(async)不等待,和非主队列的组合会默认开启线程。

1、同步+并发 = 串行
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
//MARK: => 同步+并发=串行
- (void)gcdTest1{
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"syncConcurrent---begin");
//创建同步队列。
dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_sync(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});

dispatch_sync(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});

NSLog(@"syncConcurrent---end");
}

打印结果:

1
2
3
4
5
6
7
8
9
currentThread---<NSThread: 0x600003cca900>{number = 1, name = main}
syncConcurrent---begin
1---<NSThread: 0x600003cca900>{number = 1, name = main}
1---<NSThread: 0x600003cca900>{number = 1, name = main}
2---<NSThread: 0x600003cca900>{number = 1, name = main}
2---<NSThread: 0x600003cca900>{number = 1, name = main}
3---<NSThread: 0x600003cca900>{number = 1, name = main}
3---<NSThread: 0x600003cca900>{number = 1, name = main}
syncConcurrent---end
2、异步+并发=并行
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
//MARK: => 异步+并发=并行
- (void)gcdTest2{

NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"asyncConcurrent---begin");

dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});

dispatch_async(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});

dispatch_async(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});

NSLog(@"asyncConcurrent---end");
}

打印结果:

因为追加的三个任务为异步执行,所以可先执行end。

1
2
3
4
5
6
7
8
9
currentThread---<NSThread: 0x600003959300>{number = 1, name = main}
asyncConcurrent---begin
asyncConcurrent---end
2---<NSThread: 0x60000393e3c0>{number = 4, name = (null)}
1---<NSThread: 0x600003924580>{number = 3, name = (null)}
3---<NSThread: 0x600003916780>{number = 5, name = (null)}
1---<NSThread: 0x600003924580>{number = 3, name = (null)}
2---<NSThread: 0x60000393e3c0>{number = 4, name = (null)}
3---<NSThread: 0x600003916780>{number = 5, name = (null)}
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
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
NSLog(@"syncSerial---begin");

dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);

dispatch_sync(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});

NSLog(@"syncSerial---end");

打印结果:

1
2
3
4
5
6
7
8
9
currentThread---<NSThread: 0x600002330000>{number = 1, name = main}
syncSerial---begin
1---<NSThread: 0x600002330000>{number = 1, name = main}
1---<NSThread: 0x600002330000>{number = 1, name = main}
2---<NSThread: 0x600002330000>{number = 1, name = main}
2---<NSThread: 0x600002330000>{number = 1, name = main}
3---<NSThread: 0x600002330000>{number = 1, name = main}
3---<NSThread: 0x600002330000>{number = 1, name = main}
syncSerial---end
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
 NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
NSLog(@"asyncSerial---begin");

dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);

dispatch_async(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// dispatch_async(dispatch_get_main_queue(), ^{
// sleep(10);
// NSLog(@"回到了主线程");
// });
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});

NSLog(@"asyncSerial---end");

打印结果

1
2
3
4
5
6
7
8
9
currentThread---<NSThread: 0x600001538b00>{number = 1, name = main}
asyncSerial---begin
asyncSerial---end
1---<NSThread: 0x600001540240>{number = 3, name = (null)}
1---<NSThread: 0x600001540240>{number = 3, name = (null)}
2---<NSThread: 0x600001540240>{number = 3, name = (null)}
2---<NSThread: 0x600001540240>{number = 3, name = (null)}
3---<NSThread: 0x600001540240>{number = 3, name = (null)}
3---<NSThread: 0x600001540240>{number = 3, name = (null)}

5、主队列中同步执行任务。

因为默认下面代码是在主队列中执行的。简称下面代码为任务A。
当追加任务1后,因为是同步执行,任务1需要等到任务A执行完毕后再执行任务1。而任务A的执行依赖于任务1.所以会造成死锁。

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
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
NSLog(@"syncMain---begin");

dispatch_queue_t queue = dispatch_get_main_queue();

dispatch_sync(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});

dispatch_sync(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});

dispatch_sync(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});

NSLog(@"syncMain---end");
6、主队列+异步
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
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
NSLog(@"asyncMain---begin");

dispatch_queue_t queue = dispatch_get_main_queue();

dispatch_async(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});

dispatch_async(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});

dispatch_async(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});

NSLog(@"asyncMain---end");

打印结果

效果同:串行队列+异步执行

1
2
3
4
5
6
7
8
9
currentThread---<NSThread: 0x600002b65240>{number = 1, name = main}
asyncMain---begin
asyncMain---end
1---<NSThread: 0x600002b65240>{number = 1, name = main}
1---<NSThread: 0x600002b65240>{number = 1, name = main}
2---<NSThread: 0x600002b65240>{number = 1, name = main}
2---<NSThread: 0x600002b65240>{number = 1, name = main}
3---<NSThread: 0x600002b65240>{number = 1, name = main}
3---<NSThread: 0x600002b65240>{number = 1, name = main}
7、线程通信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();

dispatch_async(queue, ^{
// 回到主线程
dispatch_async(mainQueue, ^{
// 追加在主线程中执行的任务
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
});

// 异步追加任务
for (int i = 0; i < 10; ++i) {
[NSThread sleepForTimeInterval:1]; // 模拟耗时操作
NSLog(@"1-%d--%@",i,[NSThread currentThread]); // 打印当前线程
}
});

打印结果

在全局队列中,异步添加主队列任务。
主队列任务不会阻塞全局队列的任务,但是不确定主队列任务在何时执行完。

1
2
3
4
5
6
7
8
9
10
11
1-0--<NSThread: 0x60000201bfc0>{number = 3, name = (null)}
1-1--<NSThread: 0x60000201bfc0>{number = 3, name = (null)}
2---<NSThread: 0x600002072940>{number = 1, name = main}
1-2--<NSThread: 0x60000201bfc0>{number = 3, name = (null)}
1-3--<NSThread: 0x60000201bfc0>{number = 3, name = (null)}
1-4--<NSThread: 0x60000201bfc0>{number = 3, name = (null)}
1-5--<NSThread: 0x60000201bfc0>{number = 3, name = (null)}
1-6--<NSThread: 0x60000201bfc0>{number = 3, name = (null)}
1-7--<NSThread: 0x60000201bfc0>{number = 3, name = (null)}
1-8--<NSThread: 0x60000201bfc0>{number = 3, name = (null)}
1-9--<NSThread: 0x60000201bfc0>{number = 3, name = (null)}

0x02 栅栏函数

dispatch_barrier_async函数的作用与barrier的意思相同,在进程管理中起到一个栅栏的作用,它等待所有位于barrier函数之前的操作执行完毕后执行。
值得注意的是:
1、在barrier函数执行之后,barrier函数之后的操作才会得到执行。
2、该函数需要同dispatch_queue_create函数生成的concurrent Dispatch 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
41
42
43
 //创建并发队列。
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

//栅栏函数只能在自己新建的队列中才有效。全局队列并不生效。
// dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_async(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});

dispatch_barrier_async(queue, ^{
// 追加任务 barrier
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程
}
});

dispatch_async(queue, ^{
// 追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 追加任务4
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"4---%@",[NSThread currentThread]); // 打印当前线程
}
});

打印结果

1
2
3
4
5
6
7
8
9
10
2---<NSThread: 0x6000030f5440>{number = 4, name = (null)}
1---<NSThread: 0x6000030f5400>{number = 3, name = (null)}
1---<NSThread: 0x6000030f5400>{number = 3, name = (null)}
2---<NSThread: 0x6000030f5440>{number = 4, name = (null)}
barrier---<NSThread: 0x6000030f5440>{number = 4, name = (null)}
barrier---<NSThread: 0x6000030f5440>{number = 4, name = (null)}
3---<NSThread: 0x6000030f5440>{number = 4, name = (null)}
4---<NSThread: 0x6000030f5400>{number = 3, name = (null)}
4---<NSThread: 0x6000030f5400>{number = 3, name = (null)}
3---<NSThread: 0x6000030f5440>{number = 4, name = (null)}

0x03 队列组

在使用GCD进行任务操作时,有时会希望若干个任务执行之间有先后执行的依赖关系,
例如,当A、B两个异步任务完成后,再去完成C任务,
这时就可以使用队列组dispatch group来完成。

1、组合一

dispatch_group_t
dispatch_group_async
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
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
NSLog(@"group---begin");

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});

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

打印结果

notify中的任务会在线程组的其它任务执行完成后执行。

1
2
3
4
5
6
7
8
9
currentThread---<NSThread: 0x600000f62940>{number = 1, name = main}
group---begin
2---<NSThread: 0x600000f1c480>{number = 3, name = (null)}
1---<NSThread: 0x600000f7fdc0>{number = 4, name = (null)}
2---<NSThread: 0x600000f1c480>{number = 3, name = (null)}
1---<NSThread: 0x600000f7fdc0>{number = 4, name = (null)}
3---<NSThread: 0x600000f62940>{number = 1, name = main}
3---<NSThread: 0x600000f62940>{number = 1, name = main}
group---end
2、组合二

暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。
1、dispatch_group_t
2、dispatch_group_async
3、dispatch_group_wait 阻塞。

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
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
NSLog(@"group---begin");

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务1
for (int i = 0; i < 5; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});

dispatch_queue_t queue = dispatch_queue_create("dyoung", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
// 追加任务2
for (int i = 0; i < 5; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});

// 等待上面group中的任务全部完成后,会往下继续执行(会阻塞当前线程)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

NSLog(@"group---end");

打印结果

1
2
3
4
5
6
7
8
9
10
11
12
13
currentThread---<NSThread: 0x600003d47f00>{number = 1, name = main}
group---begin
2---<NSThread: 0x600003d10bc0>{number = 4, name = (null)}
1---<NSThread: 0x600003d35300>{number = 3, name = (null)}
2---<NSThread: 0x600003d10bc0>{number = 4, name = (null)}
1---<NSThread: 0x600003d35300>{number = 3, name = (null)}
1---<NSThread: 0x600003d35300>{number = 3, name = (null)}
2---<NSThread: 0x600003d10bc0>{number = 4, name = (null)}
2---<NSThread: 0x600003d10bc0>{number = 4, name = (null)}
1---<NSThread: 0x600003d35300>{number = 3, name = (null)}
2---<NSThread: 0x600003d10bc0>{number = 4, name = (null)}
1---<NSThread: 0x600003d35300>{number = 3, name = (null)}
group---end
3、组合三

dispatch_group_enter、
dispatch_group_leave
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
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
NSLog(@"group---begin");

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, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
dispatch_group_leave(group);
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程.
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
NSLog(@"group---end");
});

打印结果

1
2
3
4
5
6
7
8
9
currentThread---<NSThread: 0x600003b2c580>{number = 1, name = main}
group---begin
2---<NSThread: 0x600003b42b00>{number = 4, name = (null)}
1---<NSThread: 0x600003b59a00>{number = 3, name = (null)}
2---<NSThread: 0x600003b42b00>{number = 4, name = (null)}
1---<NSThread: 0x600003b59a00>{number = 3, name = (null)}
3---<NSThread: 0x600003b2c580>{number = 1, name = main}
3---<NSThread: 0x600003b2c580>{number = 1, name = main}
group---end

0x04 信号量

dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量
dispatch_semaphore_signal:发送一个信号,让信号总量加1
dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。
保持线程同步,将异步执行任务转换为同步执行任务
保证线程安全,为线程加锁
信号量在等待的时候,耗能较少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//初始化信号量为1.所以wait可以写在任务前面。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i < 5; i++)
{
//必须放block外面。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(globalQueue, ^{
//先判断,再减 1。
[NSThread sleepForTimeInterval:0.5];
NSLog(@"i=%d---thread==%@\n",i,[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
}

同步打印:

1
2
3
4
5
i=0---thread==<NSThread: 0x600000e4a080>{number = 3, name = (null)}
i=1---thread==<NSThread: 0x600000e4a080>{number = 3, name = (null)}
i=2---thread==<NSThread: 0x600000e4a080>{number = 3, name = (null)}
i=3---thread==<NSThread: 0x600000e4a080>{number = 3, name = (null)}
i=4---thread==<NSThread: 0x600000e4a080>{number = 3, name = (null)}

0x05 通过信号量保证线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (void)gcdTest16{
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程

self.ticketSurplusCount = 50;

//开两个同步队列。
// queue1 代表北京火车票售卖窗口
dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
// queue2 代表上海火车票售卖窗口
dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);

//创建信号量。
_semaphoreLock = dispatch_semaphore_create(1);

// __weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
NSLog(@"1");
[self saleTicketNotSafe];//weakself没值的问题。
});
dispatch_async(queue2, ^{
NSLog(@"2");
[self saleTicketNotSafe];
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)saleTicketNotSafe {
NSLog(@"11");
while (1) {
dispatch_semaphore_wait(_semaphoreLock, DISPATCH_TIME_FOREVER);
if (self.ticketSurplusCount > 0) { //如果还有票,继续售卖
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.5];
} else { //如果已卖完,关闭售票窗口
NSLog(@"所有火车票均已售完");
break;
}
// 相当于解锁
dispatch_semaphore_signal(_semaphoreLock);
}
}

0x06 快速迭代,并发遍历

1
2
3
4
5
6
7
8
9
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"apply---begin");
//dispatch_apply 可以 在多个线程中同时(异步)遍历多个数字。
//无论是在串行队列,还是异步队列中,dispatch_apply 都会等待全部任务执行完毕
dispatch_apply(100, queue, ^(size_t index) {
[NSThread sleepForTimeInterval:0.5];
NSLog(@"%zd---%@",index, [NSThread currentThread]);
});
NSLog(@"apply---end");//这个肯定在最后执行。

打印结果

无序,但是保证每个都被遍历到。

1
2
3
4
5
6
7
8
9
apply---begin
3---<NSThread: 0x60000098a9c0>{number = 5, name = (null)}
2---<NSThread: 0x6000009a5c80>{number = 3, name = (null)}
0---<NSThread: 0x6000009c0dc0>{number = 1, name = main}
1---<NSThread: 0x600000989bc0>{number = 4, name = (null)}
·
·
·
apply---end

0x07 其它用法

延迟执行
1
2
3
4
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2.0秒后异步追加任务代码到主队列,并开始执行
NSLog(@"after---%@",[NSThread currentThread]); // 打印当前线程
});
单例用法
1
2
3
4
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"🍺🍺🍺🍺🍺 onceToken");
});

0x08 备注

更多GCD介绍参考iOS多线程:GCD详尽总结
本文Demo:DyGCDTest.m