参考:
GCD源码
深入理解 GCD
iOS多线程--彻底学会多线程之『GCD』
关于iOS多线程,我说,你听,没准你就懂了
任务执行方式(同步、异步)
一般理解
1、同步执行(dispatch_sync):只能在当前线程中执行任务,不具备开启新线程的能力。必须等到Block函数执行完毕后,dispatch函数才会返回。
2、异步执行(dispatch_async):可以在新的线程中执行任务,具备开启新线程的能力。dispatch函数会立即返回, 然后Block在后台异步执行。
高级理解
1、dispatch_sync 的实现略简单一些,它不涉及线程池(因此一般都在当前线程执行),而是利用与线程绑定的信号量来实现串行
2、dispatch_async 会把任务添加到队列的一个链表中,添加完后会唤醒队列,根据 vtable 中的函数指针,调用 wakeup 方法。在 wakeup 方法中,从线程池里取出工作线程(如果没有就新建),然后在工作线程中取出链表头部指向的 block 并执行。
任务管理方式(串行队列、并行队列)
1、串行队列:所有任务会在一条线程中执行(有可能是当前线程也有可能是新开辟的线程),并且一个任务执行完毕后,才开始执行下一个任务。(等待完成)
2、并行队列:可以开启多条线程并行执行任务(但不一定会开启新的线程),并且当一个任务放到指定线程开始执行时,下一个任务就可以开始执行了。(等待发生)
3、分发到不同队列时,代码进入的分支也不一样,比如 dispatch_async 到主队列的任务由 runloop 处理,而分发到其他队列的任务由线程池处理
// 主队列--串行,所有放在主队列中的任务,都会放到主线程中执行
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// 全局队列--并行,系统为我们创建好的一个并行队列,使用起来与我们自己创建的并行队列无本质差别
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
// new串行队列
dispatch_queue_t queue1 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// new并行队列
dispatch_queue_t queue2 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
注意:避免使用 GCD Global队列创建Runloop常驻线程
全局队列的底层是一个线程池,向全局队列中提交的 block,都会被放到这个线程池中执行,如果线程池已满,后续再提交 block 就不会再重新创建线程。等待有空闲的线程在执行任务。
所以:
避免使用 GCD Global 队列创建 Runloop 常驻线程,如果n条线程都被霸占了,Global队列就费了。
dispatch_group (组)
dispatch_group 的本质就是一个 value 非常大的信号量,等待 group 完成实际上就是等待 value 恢复初始值。而 notify 的作用是将所有注册的回调组装成一个链表,在 dispatch_async 完成时判断 value 是不是恢复初始值,如果是则调用 dispatch_async 异步执行所有注册的回调。
dispatch_once (单次)
dispatch_once 通过一个静态变量来标记 block 是否已被执行,同时使用信号量确保只有一个线程能执行,执行完 block 后会唤醒其他所有等待的线程
dispatch_barrier_async (栅栏)
dispatch_barrier_async 改变了 block 的 vtable 标记位,当它将要被取出执行时,会等待前面的 block 都执行完,然后在下一次循环中被执行
任务+队列
串行队列 | 并行队列 | 主队列 | |
---|---|---|---|
同步(sync) | 当前线程,串行执行 | 队列当前线程,串行执行 | 主新线程,串行执行(注意死锁) |
异步(async) | 开1条新线程,串行执行 | 开n条新线程,异步执行(n在iphone7上面最大是几十个) | 主新线程,串行执行 |
判断当前队列
static char *queueKey = "queueKey";
dispatch_queue_t queue1 = dispatch_queue_create(queueKey, nil);
dispatch_queue_set_specific(queue, queueKey, &queueKey, NULL); // 设置标识
dispatch_sync(queue1, ^{
if (dispatch_get_specific(queueKey)) {
//说明当前的队列就是queue1
}else{
//说明当前的队列不是是queue1
}
});
容易误解的概念
1、主线程只会执行主队列的任务--(主线程也可以执行其他队列,比如sync执行其他队列)
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_set_specific(mainQueue, "key", "main", NULL);
dispatch_sync(globalQueue, ^{
BOOL res1 = [NSThread isMainThread];
BOOL res2 = dispatch_get_specific("key") != NULL;
NSLog(@"is main thread: %zd --- is main queue: %zd", res1, res2);
});
根据正常逻辑的理解来说,这里的两个判断结果应该都是NO,但运行后,第一个判断为YES,后者为NO,输出说明了主线程此时执行了work queue的任务.
Dispatch Block
队列执行任务都是block的方式,
创建block
- (void)createDispatchBlock {
// 一般的block
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"run block");
});
dispatch_async(concurrentQueue, block);
//QOS优先级的block
dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{
NSLog(@"run qos block");
});
dispatch_async(concurrentQueue, qosBlock);
}
dispatch_block_wait:可以根据dispatch block来设置等待时间,参数DISPATCH_TIME_FOREVER会一直等待block结束
- (void)dispatchBlockWaitDemo {
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"star");
[NSThread sleepForTimeInterval:5.f];
NSLog(@"end");
});
dispatch_async(serialQueue, block);
//设置DISPATCH_TIME_FOREVER会一直等到前面任务都完成
dispatch_block_wait(block, DISPATCH_TIME_FOREVER);
NSLog(@"ok, now can go on");
}
dispatch_block_notify:可以监视指定dispatch block结束,然后再加入一个block到队列中。三个参数分别为,第一个是需要监视的block,第二个参数是需要提交执行的队列,第三个是待加入到队列中的block
- (void)dispatchBlockNotifyDemo {
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t firstBlock = dispatch_block_create(0, ^{
NSLog(@"first block start");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"first block end");
});
dispatch_async(serialQueue, firstBlock);
dispatch_block_t secondBlock = dispatch_block_create(0, ^{
NSLog(@"second block run");
});
//first block执行完才在serial queue中执行second block
dispatch_block_notify(firstBlock, serialQueue, secondBlock);
}
dispatch_block_cancel:iOS8之后可以调用dispatch_block_cancel来取消(需要注意必须用dispatch_block_create创建dispatch_block_t)
需要注意的是,未执行的可以用此方法cancel掉,若已经执行则cancel不了
如果想中断(interrupt)线程,可以使用dispatch_block_testcancel方法
- (void)dispatchBlockCancelDemo {
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t firstBlock = dispatch_block_create(0, ^{
NSLog(@"first block start");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"first block end");
});
dispatch_block_t secondBlock = dispatch_block_create(0, ^{
NSLog(@"second block run");
});
dispatch_async(serialQueue, firstBlock);
dispatch_async(serialQueue, secondBlock);
//取消secondBlock
dispatch_block_cancel(secondBlock);
}
1. 串行队列 + 同步执行
不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务
- (void)serialQueueSync{
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t serialQueue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog输出
0========{number = 3, name = (null)}
1========{number = 3, name = (null)}
2========{number = 3, name = (null)}
3========{number = 3, name = (null)}
4========{number = 3, name = (null)}
*/
}
2. 串行队列 + 异步执行
开一个新线程,一个一个执行任务
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog输出
0========{number = 1, name = main}
4========{number = 1, name = main}
1========{number = 3, name = (null)}
2========{number = 3, name = (null)}
3========{number = 3, name = (null)}
*/
3. 并行队列 + 同步执行
当前线程,一个一个执行任务
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog输出
0========{number = 3, name = (null)}
1========{number = 3, name = (null)}
2========{number = 3, name = (null)}
3========{number = 3, name = (null)}
4========{number = 3, name = (null)}
*/
4. 并行队列 + 异步执行
开多个线程,异步执行
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog输出
0========{number = 1, name = main}
4========{number = 1, name = main}
2========{number = 4, name = (null)}
1========{number = 3, name = (null)}
3========{number = 5, name = (null)}
*/
5. 主队列 + 异步执行
主线程,同步执行
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog输出
0========{number = 3, name = (null)}
4========{number = 3, name = (null)}
1========{number = 1, name = main}
2========{number = 1, name = main}
3========{number = 1, name = main}
*/
6. 主队列 + 同步执行 (不能在主队列这么用,死锁)
主线程,同步执行
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog输出
0========{number = 3, name = (null)}
1========{number = 1, name = main}
2========{number = 1, name = main}
3========{number = 1, name = main}
4========{number = 3, name = (null)}
*/
GCD其他用法
dispatch_after延时
1、time = 0,是直接调用异步dispatch_async
2、time > 0, 只是延时提交block,不是延时立刻执行。
//2秒延时、在主队列
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
});
dispatch_once与dispatch_once_t
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//
});
1、dispatch_once并不是简单的只执行一次那么简单
2、dispatch_once本质上可以接受多次请求,会对此维护一个请求链表
3、如果在block执行期间,多次进入调用同类的dispatch_once函数(即单例函数),会导致整体链表无限增长,造成永久性死锁。
递归互相嵌套,如下:
- (void)viewDidLoad {
[super viewDidLoad];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ // 链表无限增长
[self viewDidLoad];
});
}
dispatch_once源码
static void
dispatch_once_f_slow(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
#if DISPATCH_GATE_USE_FOR_DISPATCH_ONCE
dispatch_once_gate_t l = (dispatch_once_gate_t)val;
if (_dispatch_once_gate_tryenter(l)) {
_dispatch_client_callout(ctxt, func);
_dispatch_once_gate_broadcast(l);
} else {
_dispatch_once_gate_wait(l);
}
#else
_dispatch_once_waiter_t volatile *vval = (_dispatch_once_waiter_t*)val;
struct _dispatch_once_waiter_s dow = { };
_dispatch_once_waiter_t tail = &dow, next, tmp;
dispatch_thread_event_t event;
if (os_atomic_cmpxchg(vval, NULL, tail, acquire)) {
// 第一次dispatch_once,原子性操作
// 当前线程
dow.dow_thread = _dispatch_tid_self();
// 执行block
_dispatch_client_callout(ctxt, func);
// 第一次执行完了,设置token = DISPATCH_ONCE_DONE
next = (_dispatch_once_waiter_t)_dispatch_once_xchg_done(val);
while (next != tail) {
// 继续去下一个
tmp = (_dispatch_once_waiter_t)_dispatch_wait_until(next->dow_next);
event = &next->dow_event;
next = tmp;
// 信号量++
_dispatch_thread_event_signal(event);
}
} else {
// 第二次dispatch_once进来
_dispatch_thread_event_init(&dow.dow_event);
next = *vval;
for (;;) {
if (next == DISPATCH_ONCE_DONE) { // token是否等于DISPATCH_ONCE_DONE
// 第一次执行完之后,都是走这里
break;
}
// 如果是嵌套使用,第一次没有完成,又要执行一次
if (os_atomic_cmpxchgv(vval, next, tail, &next, release)) {
// 原子性
dow.dow_thread = next->dow_thread;
dow.dow_next = next;
if (dow.dow_thread) {
pthread_priority_t pp = _dispatch_get_priority();
_dispatch_thread_override_start(dow.dow_thread, pp, val);
}
// 等待信号量
_dispatch_thread_event_wait(&dow.dow_event);
if (dow.dow_thread) {
_dispatch_thread_override_end(dow.dow_thread, val);
}
break;
}
}
_dispatch_thread_event_destroy(&dow.dow_event);
}
#endif
}
dispatch_apply(count,queue,block(index))迭代方法
该函数按指定的次数将指定的block追加到指定的队列;使用的地方,阻塞当前线程
NSLog(@"CurrentThread------%@", [NSThread currentThread]);
//dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 6是次数
dispatch_apply(6, queue, ^(size_t index) {
NSLog(@"%zd------%@",index, [NSThread currentThread]);
});
/*
并发队列:开多线程异步执行
NSLogx信息
CurrentThread------{number = 3, name = (null)}
0------{number = 3, name = (null)}
1------{number = 4, name = (null)}
2------{number = 5, name = (null)}
3------{number = 6, name = (null)}
4------{number = 3, name = (null)}
5------{number = 4, name = (null)}
*/
/*
同步队列:当前线程同步执行
NSLogx信息
CurrentThread------{number = 3, name = (null)}
0------{number = 1, name = main}
1------{number = 1, name = main}
2------{number = 1, name = main}
3------{number = 1, name = main}
4------{number = 1, name = main}
5------{number = 1, name = main}
*/
dispatch_apply能避免线程爆炸,因为GCD会管理并发
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 999; i++){
dispatch_async(queue, ^{
NSLog(@"%d,%@",i,[NSThread currentThread]);// 能开多大线程就开多大线程(几十个)
});
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(999, queue, ^(size_t i){
NSLog(@"%d,%@",i,[NSThread currentThread]); // 只开一定数量的线程(几个)
});
dispatch_suspend、dispatch_resume (用在dispatch_get_global_queue主队列无效)
dispatch_suspend,dispatch_resume提供了“挂起、恢复”队列的功能,简单来说,就是可以暂停、恢复队列上的任务。但是这里的“挂起”,并不能保证可以立即停止队列上正在运行的block
注意点:
1、如果队列没有使用dispatch_suspend,使用dispatch_resume会crash
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_resume(queue); // crash
}
2、如果queue被挂起,queue销毁时候没有被唤醒,会crash
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_suspend(queue);// 如果queue被挂起,queue销毁时候没有被唤醒,会crash
3、dispatch_suspend后面执行dispatch_sync,阻塞当前线程,需要其他线程恢复队列
queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_suspend(queue);
// 后面执行dispatch_sync,阻塞当前线程,需要其他线程恢复队列
dispatch_sync(queue, ^{
NSLog(@"=====%@",@"111111");
});
NSLog(@"=====%@",@"22222");
GCD的队列组dispatch_group_t,其实就是封装了一个无限大的信号量,
注意事项
1、dispatch_group_async(只有async,无sync)等价于{dispatch_group_enter() + async}, async调用完了会执行dispatch_group_leave()。
2、dispatch_group_enter()就是信号量--;
3、dispatch_group_leave()就是信号量++
4、dispatch_group_enter() 必须运行在 dispatch_group_leave() 之前。
5、dispatch_group_enter() 和 dispatch_group_leave() 需要成对出现的
//1.创建队列组
dispatch_group_t group = dispatch_group_create();
//2.1.全局队列
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-01 - %@", [NSThread currentThread]);
}
});
//2.2.主队列
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-02 - %@", [NSThread currentThread]);
}
});
//2.3.自建串行队列
dispatch_group_async(group, dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL), ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-03 - %@", [NSThread currentThread]);
}
});
//3.都完成后会自动通知,不阻塞当前线程
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
NSLog(@"当前线程=====%@",[NSThread currentThread]);
/*
并行队列、自建串行队列的任务多线程异步执行
主队列的任务主线程同步执行,且排在全部任务的最后
NSLog信息
当前线程====={number = 1, name = main}
group-01 - {number = 3, name = (null)}
group-01 - {number = 3, name = (null)}
group-03 - {number = 4, name = (null)}
group-01 - {number = 3, name = (null)}
group-03 - {number = 4, name = (null)}
group-03 - {number = 4, name = (null)}
group-02 - {number = 1, name = main}
group-02 - {number = 1, name = main}
group-02 - {number = 1, name = main}
完成 - {number = 1, name = main}
*/
手动标记group完成
- dispatch_group_enter(group)
- dispatch_group_leave(group);
dispatch_group_notify(不阻塞)相当于把block任务加在最后
NSLog(@"start");
//1.创建队列组
dispatch_group_t group = dispatch_group_create();
for (int i=0; i< 5; i++) {
dispatch_group_enter(group); // enter
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// something
NSLog(@"something===%zd",i);
dispatch_group_leave(group); // eave
});
}
// 都完成后会自动通知,不阻塞当前线程
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
NSLog(@"end");
/*
NSLog信息:
start
end
something===1
something===0
something===2
something===3
something===4
完成 - {number = 1, name = main}
*/
dispatch_group_wait就是等待group的信号量回到初始值(阻塞当前线程)
NSLog(@"start");
//1.创建队列组
dispatch_group_t group = dispatch_group_create();
for (int i=0; i< 5; i++) {
dispatch_group_enter(group); // enter
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// something
[NSThread sleepForTimeInterval:2];
NSLog(@"something===%zd",i);
dispatch_group_leave(group); // eave
});
}
// 阻塞当前线程的、等待5秒
dispatch_time_t waitTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
dispatch_group_wait(group, waitTime);//特殊值有:DISPATCH_TIME_FOREVER是无限等,DISPATCH_TIME_NOW是不等
NSLog(@"end");
/*
等待时间 < 执行需要时间
NSLog信息:
start
end
something===0
something===1
something===3
something===2
something===4
*/
/*
等待时间 > 执行需要时间
NSLog信息:
start
something===1
something===0
something===4
something===2
something===3
end
*/
dispatch_semaphore_create & dispatch_semaphore_signal & dispatch_semaphore_wait
信号量是控制任务执行的重要条件,当信号量为0时,所有任务等待,信号量越大,允许可并行执行的任务数量越多。
- dispatch_semaphore_create(long value);创建信号量,初始值不能小于0;value信号数值
- dispatch_semaphore_wait(semaphore, timeout);等待降低信号量,也就是信号量-1;timeout不是调用dispatch_semaphore_wait后等待的时间,而是信号量创建后的时间
- dispatch_semaphore_signal(semaphore);提高信号量,也就是信号量+1;
- dispatch_semaphore_wait和dispatch_semaphore_signal通常配对使用。
// 相当于控制新建的线程数
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
for (int i=0; i< 10; i++) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
}
/*
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
只有5条线程
NSLog信息:
第0次_{number = 4, name = (null)}
第1次_{number = 6, name = (null)}
第3次_{number = 5, name = (null)}
第2次_{number = 3, name = (null)}
第4次_{number = 7, name = (null)}
第8次_{number = 3, name = (null)}
第7次_{number = 5, name = (null)}
第6次_{number = 4, name = (null)}
第5次_{number = 6, name = (null)}
第9次_{number = 7, name = (null)}
*/
/*
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
10条线程
NSLog信息:
第2次_{number = 3, name = (null)}
第4次_{number = 7, name = (null)}
第1次_{number = 4, name = (null)}
第5次_{number = 8, name = (null)}
第3次_{number = 6, name = (null)}
第0次_{number = 5, name = (null)}
第6次_{number = 9, name = (null)}
第9次_{number = 10, name = (null)}
第7次_{number = 11, name = (null)}
第8次_{number = 12, name = (null)}
*/
dispatch_barrier_async、dispatch_barrier_sync (承上启下--用于自建的并行队列)
保证此前的任务都先于自己执行,此后的任务也迟于自己执行。
dispatch_barrier_async 不阻塞当前线程;
dispatch_barrier_sync 阻塞当前线程;
注意:dispatch_barrier_(a)sync只在自己创建的并发队列上有效,在全局(Global)并发队列、串行队列上,效果跟dispatch_(a)sync效果一样。
- (void)test{
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_async(globalQueue, ^{
NSLog(@"任务1");
});
dispatch_async(globalQueue, ^{
NSLog(@"任务2");
});
dispatch_barrier_async(globalQueue, ^{
NSLog(@"任务barrier");
});
dispatch_async(globalQueue, ^{
NSLog(@"任务3");
});
dispatch_async(globalQueue, ^{
NSLog(@"任务4");
});
/*
2017-09-02 21:03:40.255 NSThreadTest[28856:21431532] 任务2
2017-09-02 21:03:40.255 NSThreadTest[28856:21431535] 任务1
2017-09-02 21:03:40.255 NSThreadTest[28856:21431533] 任务barrier
2017-09-02 21:03:40.255 NSThreadTest[28856:21431551] 任务3
2017-09-02 21:03:40.256 NSThreadTest[28856:21431550] 任务4
*/
}
GCD创建Timer
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.创建一个GCD定时器
/*
第一个参数:表明创建的是一个定时器
第四个参数:队列
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 需要对timer进行强引用,保证其不会被释放掉,才会按时调用block块
// 局部变量,让指针强引用
self.timer = timer;
//2.设置定时器的开始时间,间隔时间,精准度
/*
第1个参数:要给哪个定时器设置
第2个参数:开始时间
第3个参数:间隔时间
第4个参数:精准度 一般为0 在允许范围内增加误差可提高程序的性能
GCD的单位是纳秒 所以要*NSEC_PER_SEC
*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//3.设置定时器要执行的事情
dispatch_source_set_event_handler(timer, ^{
NSLog(@"---%@--",[NSThread currentThread]);
});
// 启动
dispatch_resume(timer);
}
GCD各种死锁的情况
1、最常见的
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"0========%@",[NSThread currentThread]);// 当前是主线程、主队列
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
NSLog(@"2========%@",[NSThread currentThread]);
/*
NSLog信息
0========{number = 1, name = main}
解析
1.viewDidLoad是主队列,dispatch_sync也属于主队列,
2.dispatch_sync是viewDidLoad里面的代码,viewDidLoad需要等待dispatch_sync执行完,dispatch_sync需要等待viewDidLoad执行完,这就死锁了
*/
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"0========%@",[NSThread currentThread]);// 当前是主线程、主队列
// 改成dispatch_get_global_queue或者new出来的队列
dispatch_sync(dispatch_get_global_queue(0,0), ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
NSLog(@"2========%@",[NSThread currentThread]);
/*
NSLog信息
0========{number = 1, name = main}
1========{number = 1, name = main}
2========{number = 1, name = main}
解析
1.viewDidLoad是主队列,dispatch_sync是global_queue,不在同一队列
2.dispatch_sync是viewDidLoad里面的代码,viewDidLoad需要等待dispatch_sync执行完返回,但是dispatch_sync不需要等待viewDidLoad执行完,立即执行完返回
*/
}
2、串行队列,各种嵌套异步情况
死锁的原因:是同一个串行队列任务内部代码继续嵌套同步sync的任务
// 串行队列
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
// 同步嵌异步----执行OK
dispatch_sync(queue, ^{
dispatch_async(queue, ^{
});
});
// 异步嵌异步----执行OK
dispatch_async(queue, ^{
dispatch_async(queue, ^{
});
});
// 异步嵌同步----死锁
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
});
});
// 同步嵌同步----死锁
dispatch_sync(queue, ^{
dispatch_sync(queue, ^{
});
});
3、并行队列,各种嵌套异步情况
并行队列队列各种嵌套都不会死锁
// 并行队列
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
// 同步嵌异步----执行OK
dispatch_sync(queue, ^{
dispatch_async(queue, ^{
});
});
// 异步嵌异步----执行OK
dispatch_async(queue, ^{
dispatch_async(queue, ^{
});
});
// 异步嵌同步----执行OK
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
});
});
// 同步嵌同步----执行OK
dispatch_sync(queue, ^{
dispatch_sync(queue, ^{
});
});
4、dispatch_apply阻塞当前线程
// 主队列使用,死锁
dispatch_apply(5, dispatch_get_main_queue(), ^(size_t i) {
NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
});
// 嵌套使用,死锁
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, queue, ^(size_t) {
// 任务
...
dispatch_apply(10, queue, ^(size_t) {
// 任务
...
});
});
dispatch_barrier
dispatch_barrier_sync在串行队列和全局并行队列里面和dispatch_sync同样的效果,所以需考虑同dispatch_sync一样的死锁问题。
5、 信号量阻塞主线程
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"semaphore create!");
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(semaphore);
NSLog(@"semaphore plus 1");
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore minus 1");
}
原因:
如果当前执行的线程是主线程,以上代码就会出现死锁。
因为dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)阻塞了当前线程,而且等待时间是DISPATCH_TIME_FOREVER——永远等待,这样它就永远的阻塞了当前线程——主线程。导致主线中的dispatch_semaphore_signal(semaphore)没有执行,
而dispatch_semaphore_wait一直在等待dispatch_semaphore_signal改变信号量,这样就形成了死锁。
解决方法:
应该将信号量移到并行队列中,如全局调度队列。以下场景,移到串行队列也是可以的。但是串行队列还是有可能死锁的(如果执行dispatch_semaphore_signal方法还是在对应串行队列中的话,即之前提到的串行队列嵌套串行队列的场景)。
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"semaphore create!");
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(semaphore); // +1
NSLog(@"semaphore plus 1");
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore minus 1");
});
}
一些嵌套使用问题
NSLog(@"1");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
dispatch_async(dispatch_get_main_queue(), ^{
sleep(1);
NSLog(@"3");
});
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"4");
});
NSLog(@"5");
//结果是:12534
//解析:“2”并发队列同步任务,所以125;“3”、“4”是两个主队列异步,串行执行任务34;最终就是12534
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^{
dispatch_sync(queue, ^{
NSLog(@"B");
});
NSLog(@"A");
});
// 结果是:BA (并发队列不会死锁) 并行队列,任务A加入队列执行中,然后任务B加入队列也立即执行,但是任务A会等任务B先执行完。