dispatch_group
从名字上也知道他是一个组,他的用法就是把一组任务提交到队列中,然后可以监听这些任务.
常见的方法:
dispatch_group_create
创建一个调度任务组dispatch_group_async
把一个任务异步提交到任务组里dispatch_group_enter/dispatch_group_leave
这种方式用在不使用dispatch_group_async来提交任务,且必须配合使用dispatch_group_notify
用来监听任务组事件的执行完毕dispatch_group_wait
设置等待时间,在等待时间结束后,如果还没有执行完任务组,则返回。返回0代表执行成功,非0则执行失败.稍后会提到semaphore的问题
实际的使用场景
现在有4个任务: 任务1、任务2、任务3、任务4. 且任务3必须在任务2之后,任务4必须在前3个任务都执行完成后,才能执行,并且需要在主线程更新UI。也就是说要控制他们的顺序.
那么也就是:
任务3必须在任务2之后,所以这两个必须串行执行,同时,任务2和3整体可以和任务1并行执行,最后,任务4只能等待前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
-(void)disGroup{
dispatch_queue_t globalQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t selfQuene = dispatch_queue_create("myQuene", 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, globalQuene, ^{
NSLog(@"run task 1");
});
dispatch_group_async(group, selfQuene, ^{
NSLog(@"run task 2");
});
dispatch_group_async(group, selfQuene, ^{
NSLog(@"run task 3");
});
// 这个notify是等到 Group 中都执行完了才会执行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"run task 4");
});
}
2019-01-16 09:56:06.990423+0800 GCD[438:139258] run task 1
2019-01-16 09:56:06.990509+0800 GCD[438:139260] run task 2
2019-01-16 09:56:06.990523+0800 GCD[438:139260] run task 3
2019-01-16 09:56:07.013527+0800 GCD[438:139229] run task 4
总结: 1和(2、3)是并行执行关系,2、3是串行执行关系,且3肯定在2之后,而4在(1、2、3)全部完成之后才会执行。
dispatch_group_enter(group)、dispatch_group_leave(group)
这个用的很少…
这两个方法其实就是:手动管理group关联的block的运行状态(或计数),并且使用时必须保证进入和退出group次数匹配。 AB 其实一样的.
1
2
3
4
5
6
7
8
9
10
11
A)
dispatch_group_async(group, queue, ^{
// 。。。
});
B)
dispatch_group_enter(group);
dispatch_async(queue, ^{
//。。。
dispatch_group_leave(group);
});
例子:有3个异步请求任务,任务1、2、3,在3个任务全部完成之后,需要执行任务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
dispatch_queue_t globalQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
//任务1
dispatch_group_enter(group);
dispatch_async(globalQuene, ^{
NSLog(@"run task 1");
sleep(1);
dispatch_group_leave(group);
});
//任务2
dispatch_group_enter(group);
dispatch_async(globalQuene, ^{
NSLog(@"run task 2");
sleep(2);
dispatch_group_leave(group);
});
//任务3
dispatch_group_enter(group);
dispatch_async(globalQuene, ^{
NSLog(@"run task 3");
sleep(3);
dispatch_group_leave(group);
});
//一直等待完成
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//任务3
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"run task 4");
});
注意看: 这三个任务是异步执行的. 即使顺序可能不同,(当然也可以用串行队列)但可以肯定的是一共用了3秒时间.之后执行4
1
2
3
4
2019-01-16 10:00:42.126815+0800 GCD[443:140782] run task 1
2019-01-16 10:00:42.126848+0800 GCD[443:140780] run task 2
2019-01-16 10:00:42.126864+0800 GCD[443:140783] run task 3
2019-01-16 10:00:45.182753+0800 GCD[443:140756] run task 4
dispatch_semaphore
先来看看dispatch_semaphore
能解决什么问题
假设现在系统有两个空闲资源可以被利用,但同一时间却有三个线程要进行访问,这种情况下,该如何处理
我们要下载很多图片,并发异步进行,每个下载都会开辟一个新线程,可是我们又担心太多线程肯定cpu吃不消,那么我们这里也可以用信号量控制一下最大开辟线程数。
定义:
信号量:就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。其实类似于锁的机制,不过信号量都是系统处理.
信号量主要的三个函数:
1
2
3
4
5
6
7
8
//创建信号量,参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_create(信号量值)
//等待降低信号量
dispatch_semaphore_wait(信号量,等待时间)
//提高信号量
dispatch_semaphore_signal(信号量) 注意,正常的使用顺序是先降低然后再提高,这两个函数通常成对使用。
我们试着来解决第一个问题:
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)dispatchSignal{
//crate的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);
});
}
2019-01-16 10:01:40.590603+0800 GCD[445:141270] run task 1
2019-01-16 10:01:40.590659+0800 GCD[445:141277] run task 2
2019-01-16 10:01:41.595787+0800 GCD[445:141270] complete task 1
2019-01-16 10:01:41.595787+0800 GCD[445:141277] complete task 2
2019-01-16 10:01:41.596065+0800 GCD[445:141269] run task 3
2019-01-16 10:01:42.601226+0800 GCD[445:141269] complete task 3
来分析一下,异步执行所以顺序可能不同.task1 先 wait
一次,即信号量-1
,此时为1
,打印run task1
而 task2 此时也在执行,也wait
一次,即-1
,此时为0
.打印run task2
.此时进入等待状态,所以task3
并不能执行.而是等到 task1,或者 task2 完成之后,(complete task1 or conplete task2
)执行signal
即发送信号.此时进行+1
操作,此时总信号量已不为0
可以继续执行. task3 这时候才能开始执行.
如果初始值 creat 1 呢?
1
2
3
4
5
6
2019-01-16 10:02:29.081797+0800 GCD[447:141720] run task 1
2019-01-16 10:02:30.087030+0800 GCD[447:141720] complete task 1
2019-01-16 10:02:30.087359+0800 GCD[447:141718] run task 2
2019-01-16 10:02:31.092535+0800 GCD[447:141718] complete task 2
2019-01-16 10:02:31.092747+0800 GCD[447:141719] run task 3
2019-01-16 10:02:32.097998+0800 GCD[447:141719] complete task 3
可以看得出,由于信号量的限制,总是信号量大于0时才能继续. 那如果初始值 creat 3 呢? 噗…不用看也知道了,最多才开三个线程,也就相当于没有进行信号量的限制.
1
2
3
4
5
6
2019-01-16 10:03:05.737471+0800 GCD[450:142158] run task 1
2019-01-16 10:03:05.737522+0800 GCD[450:142157] run task 3
2019-01-16 10:03:05.737517+0800 GCD[450:142160] run task 2
2019-01-16 10:03:06.742733+0800 GCD[450:142157] complete task 3
2019-01-16 10:03:06.742733+0800 GCD[450:142160] complete task 2
2019-01-16 10:03:06.742733+0800 GCD[450:142158] complete task 1