Grand Control Dispatch Cookbook

Grand Control Dispatch的常见实用场景。

1. 同步转异步

最常见的用法,耗时长的操作放在Global Queue中执行,完成之后将结果交还给主线程处理(特别是UI操作):

1
2
3
4
5
6
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
	id result = [self longTimeTask];
	dispatch_async(dispatch_get_main_queue(), ^{
		[self handleResultOnMainThread:result];
	});
});

2. 异步转同步

注意:如果当前是在主线程的话,会导致UI流程卡顿。

1
2
3
4
5
6
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self someAsyncTaskWithCallback:^(BOOL successed) {
      dispatch_semaphore_signal(semaphore);
}];

dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

3. 延时操作

1
2
3
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
	[self performOnMainThread];
});

4. 单例

为类增加一个类方法,后续访问单例就用MyClass.sharedIncstance

注意,MyClass的初始化方法里面init一定不要重复调用MyClass.sharedIncstance,或者与其他单例初始化互相调用,会造成循环引用。

1
2
3
4
5
6
7
8
9
10
11
+ (instancetype)sharedInstance{
    static dispatch_once_t once;
    static MyClass *sharedInstance;

    dispatch_once(&once, ^
    {
        sharedInstance = [[MyClass alloc] init];
    });
       
    return sharedInstance;
}

5. 等待多个异步任务全部完成(多个任务的等待)

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
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
	dispatch_group_enter(group);
	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
		NSLog(@"Block1a %@",[NSThread currentThread]);
		sleep(5);
		dispatch_group_leave(group);
		NSLog(@"Block1b %@",[NSThread currentThread]);
	});
});

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
	dispatch_group_enter(group);
	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
		NSLog(@"Block2a %@",[NSThread currentThread]);
		sleep(3);
		dispatch_group_leave(group);
		NSLog(@"Block2b %@",[NSThread currentThread]);
	});
});

dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
	NSLog(@"Block3 %@",[NSThread currentThread]);
});

6. 控制并发数(任务池)

比如计划下载任务有a个,但同一时间内,只有b个在工作中(b<a)。每当某个工作中的任务完成,就有一个新的计划下载任务开始执行。起到控制阀的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);

dispatch_group_t group = dispatch_group_create();

for (int i = 0; i < 10; i++) {
    dispatch_group_async(group, queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"%d --- begin ---", i + 1);
        // 线程操作区域 最多有有限个线程在此做事情
        sleep(arc4random()%5+2);
        
        NSLog(@"%d --- end ---", i + 1);
        
        dispatch_semaphore_signal(semaphore);
    });
}

// group任务全部执行完毕回调
dispatch_group_notify(group, queue, ^{
    NSLog(@"all done");
});

7. 任务插队

需要将特定的任务在一个队列的中间位置执行。可以用dispatch_barrier_async或者dispatch_barrier_sync,两者的区别是,后者会阻塞当前线程。另,根据文档,queue必须是dispatch_queue_create创建的并行队列,如果是串行队列或者全局并行队列,函数的表现与dispatch_async或者dispatch_sync一样,也就是插队失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
dispatch_queue_t queue = dispatch_queue_create("thread", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    sleep(3);
    NSLog(@"1");
});
dispatch_async(queue, ^{
    NSLog(@"2");
});

dispatch_barrier_async(queue, ^{  // 这里替换为dispatch_barrier_sync
    sleep(10);
    NSLog(@"barrier, %d",[NSThread isMainThread]);
});

dispatch_async(queue, ^{
    NSLog(@"3");
});

dispatch_async(queue, ^{
    NSLog(@"4");
});

Updated: