iOS开发

iOS - GCD

2018-09-20  本文已影响627人  昶博

目录

GCD简介

Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。在 Mac OS X 10.6 雪豹中首次推出,也可在 iOS 4 及以上版本使用。

脑图

GCD整体结构
GCD相关的一些基本概念
GCD 信号量:dispatch_semaphore

优点

缺点:

GCD核心概念

任务和队列

同步执行(sync):
1. 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
2. 只能在当前线程中执行任务,不具备开启新线程的能力。
异步执行(async):
1. 异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
2. 可以在新的线程中执行任务,具备开启新线程的能力。
通俗解释:
人物:你,我;
事件:吃饭;
同步:你叫我吃饭,我听到回复你,然后一起去吃饭。如果我没有听到或者回复你,你就会一直叫我去吃饭,一直等我,直到我和你一起去吃饭为止。
异步:你去吃饭,喊了我一声,然后自己直接去吃饭了,我的行为不会对你造成影响。我可以和你一起去吃,也可以等我把手头的事情忙完再去吃。这之间你可以完成很多事情,不会受到我的影响。

区别:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。

串行队列(Serial Dispatch Queue):每次只执行一个任务,当前一个任务执行完成后才执行下一个任务。
并发队列(Concurrent Dispatch Queue):多个任务并发执行,所以先执行的任务可能最后才完成(因为具体的执行过程导致)。

区别:执行顺序不同,以及开启线程数不同。
具体区别如下两图所示(图片来源网络):

串行队列
并行队列
关于同步异步、串行并行和线程的关系,下面通过一个表格来总结:

由上图我们可以得出我们有3种队列,2种任务执行方式,那么我们就有了6种不同的组合方式,分别是:

1.并行队列 + 同步执行
2.并行队列 + 异步执行
3.串行队列 + 同步执行
4.串行队列 + 异步执行
5.主队列 + 同步执行
6.主队列 + 异步执行

那么这几种不同组合方式各有什么区别:

队列 并发队列 串行队列 主队列
同步 (sync) 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务 主线程调用:死锁卡住不执行其他线程调用:没有开启新线程,串行执行任务
异步 (async) 有开启新线程,并发执行任务 有开启新线程(1条),串行执行任务 没有开启新线程,串行执行任务

需要注意的几个地方:

GCD队列的使用

// 串行队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("net.test.queue", DISPATCH_QUEUE_SERIAL);
// 并行队列的创建方法
dispatch_queue_t queue= dispatch_queue_create("net.test.queue", DISPATCH_QUEUE_CONCURRENT);
// 全局并行队列。GCD默认提供了全局的并行队列,需要传入两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用0即可
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    });
主队列:
dispatch_queue_t queue = dispatch_get_main_queue();
并发队列:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 同步执行任务创建方法
dispatch_sync(queue, ^{
 // 执行的任务
    NSLog(@"%@",[NSThread currentThread]);   
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
 // 执行的任务
    NSLog(@"%@",[NSThread currentThread]);    
});

需要注意的几个地方:

  1. 所有放在主队列中的任务,都会放到主线程中执行。
  2. GCD 默认提供了全局并发队列(Global Dispatch Queue)。
//并发队列 + 同步执行
- (void) syncConcurrent
{
    NSLog(@"begin-- 并发队列 + 同步执行");
    dispatch_queue_t queue= dispatch_queue_create("net.test.queue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"end-- 并发队列 + 同步执行");
}

输出的结果:
2018-09-20 17:15:06.116567+0800 GCDDemo[2295:1059495] begin-- 并发队列 + 同步执行
2018-09-20 17:15:06.116978+0800 GCDDemo[2295:1059495] 1------<NSThread: 0x60000250d3c0>{number = 1, name = main}
2018-09-20 17:15:06.117130+0800 GCDDemo[2295:1059495] 1------<NSThread: 0x60000250d3c0>{number = 1, name = main}
2018-09-20 17:15:06.117269+0800 GCDDemo[2295:1059495] 2------<NSThread: 0x60000250d3c0>{number = 1, name = main}
2018-09-20 17:15:06.117387+0800 GCDDemo[2295:1059495] 2------<NSThread: 0x60000250d3c0>{number = 1, name = main}
2018-09-20 17:15:06.117467+0800 GCDDemo[2295:1059495] 3------<NSThread: 0x60000250d3c0>{number = 1, name = main}
2018-09-20 17:15:06.117536+0800 GCDDemo[2295:1059495] 3------<NSThread: 0x60000250d3c0>{number = 1, name = main}
2018-09-20 17:15:06.117609+0800 GCDDemo[2295:1059495] end-- 并发队列 + 同步执行

从打印中可以总结如下几点:

疑问点:并发队列具备开启多个线程能力为什么没不能同时执行任务呢?
:虽然并发队列可以开启多个线程,并且可以同时执行多个任务。但是其本身不能创建新线程,因为同步任务不具备开启新线程的能力,所以只有当前线程这一个线程,所以也就不存在并发。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后,才能继续接着执行下面的操作(同步任务需要等待队列的任务执行结束)。所以任务只能一个接一个按顺序执行,不能同时被执行。

2.并发队列 + 异步执行
特点:可以开启多个线程,任务交替(同时)执行。

// 并发队列 + 异步执行
- (void) asyncConcurrent
{
    NSLog(@"begin-- 并发队列 + 异步执行");
    dispatch_queue_t queue= dispatch_queue_create("net.test.queue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"end-- 并发队列 + 异步执行");
}

输出的结果:
2018-09-20 17:41:42.367277+0800 GCDDemo[2336:1126287] begin-- 并发队列 + 异步执行
2018-09-20 17:41:42.367446+0800 GCDDemo[2336:1126287] end-- 并发队列 + 异步执行
2018-09-20 17:41:42.367505+0800 GCDDemo[2336:1126603] 2------<NSThread: 0x60000083a280>{number = 4, name = (null)}
2018-09-20 17:41:42.367517+0800 GCDDemo[2336:1126606] 3------<NSThread: 0x600000827d00>{number = 5, name = (null)}
2018-09-20 17:41:42.367522+0800 GCDDemo[2336:1126604] 1------<NSThread: 0x600000827c00>{number = 3, name = (null)}
2018-09-20 17:41:42.367601+0800 GCDDemo[2336:1126603] 2------<NSThread: 0x60000083a280>{number = 4, name = (null)}
2018-09-20 17:41:42.367627+0800 GCDDemo[2336:1126606] 3------<NSThread: 0x600000827d00>{number = 5, name = (null)}
2018-09-20 17:41:42.367653+0800 GCDDemo[2336:1126604] 1------<NSThread: 0x600000827c00>{number = 3, name = (null)}

从打印中可以总结如下几点:

3.串行队列 + 同步执行
特点:不会开启新线程,在当前线程执行任务,并且任务是串行的,执行完一个任务,再执行下一个任务。

// 串行队列 + 同步执行
- (void) syncSerial
{
    NSLog(@"begin-- 串行队列 + 同步执行");
    dispatch_queue_t queue = dispatch_queue_create("net.test.queue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"end-- 串行队列 + 同步执行");
}

输出的结果:
2018-09-21 09:14:51.305711+0800 GCDDemo[1205:63899] begin-- 串行队列 + 同步执行
2018-09-21 09:14:51.305958+0800 GCDDemo[1205:63899] 1------<NSThread: 0x6000005953c0>{number = 1, name = main}
2018-09-21 09:14:51.306117+0800 GCDDemo[1205:63899] 1------<NSThread: 0x6000005953c0>{number = 1, name = main}
2018-09-21 09:14:51.306202+0800 GCDDemo[1205:63899] 2------<NSThread: 0x6000005953c0>{number = 1, name = main}
2018-09-21 09:14:51.306292+0800 GCDDemo[1205:63899] 2------<NSThread: 0x6000005953c0>{number = 1, name = main}
2018-09-21 09:14:51.306431+0800 GCDDemo[1205:63899] 3------<NSThread: 0x6000005953c0>{number = 1, name = main}
2018-09-21 09:14:51.306516+0800 GCDDemo[1205:63899] 3------<NSThread: 0x6000005953c0>{number = 1, name = main}
2018-09-21 09:14:51.306626+0800 GCDDemo[1205:63899] end-- 串行队列 + 同步执行

从打印中可以总结如下几点:

4.串行队列 + 异步执行
特点:会开启新线程,但是因为任务是串行的,所以执行完一个任务之后,才会再执行下一个任务。

//串行队列 + 异步执行
- (void)asyncSerial {

       NSLog(@"begin-- 串行队列 + 异步执行");
    dispatch_queue_t queue = dispatch_queue_create("net.test.queue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
               NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
               NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
       NSLog(@"3------%@",[NSThread currentThread]);
        }
    });  
    NSLog(@"end-- 串行队列 + 异步执行");

}

输出的结果:
2018-09-21 09:35:45.691409+0800 GCDDemo[1363:134940] begin-- 串行队列 + 异步执行
2018-09-21 09:35:45.691612+0800 GCDDemo[1363:134940] end-- 串行队列 + 异步执行
2018-09-21 09:35:45.691686+0800 GCDDemo[1363:135267] 1------<NSThread: 0x6000016fc980>{number = 3, name = (null)}
2018-09-21 09:35:45.691787+0800 GCDDemo[1363:135267] 1------<NSThread: 0x6000016fc980>{number = 3, name = (null)}
2018-09-21 09:35:45.691886+0800 GCDDemo[1363:135267] 2------<NSThread: 0x6000016fc980>{number = 3, name = (null)}
2018-09-21 09:35:45.691959+0800 GCDDemo[1363:135267] 2------<NSThread: 0x6000016fc980>{number = 3, name = (null)}
2018-09-21 09:35:45.692040+0800 GCDDemo[1363:135267] 3------<NSThread: 0x6000016fc980>{number = 3, name = (null)}
2018-09-21 09:35:45.692107+0800 GCDDemo[1363:135267] 3------<NSThread: 0x6000016fc980>{number = 3, name = (null)}

从打印中可以总结如下几点:

5.主队列 + 同步执行
特点:1.主线程调用:互等卡主不执行。 2.其他线程调用:不会开启新线程,执行完一个任务,再执行下一个任务。

//主队列 + 同步执行  
- (void)syncMain
{
    NSLog(@"begin-- 主队列 + 同步执行");
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"end-- 主队列 + 同步执行");
}

输出的结果:
2018-09-21 10:02:33.294677+0800 GCDDemo[1563:202647] begin-- 主队列 + 同步执行
(lldb) 

从打印中可以总结如下:

疑问点:为什么任务没有执行完,而且程序了崩溃呢?
:我们在主线程中执行syncMain方法,相当于把syncMain任务放到了主线程的队列中,主线程正在处理syncMain这个任务syncMain方法中又有同步事件需要处理 ,造成会相互等待,所以死锁了,所以任务不会执行完。

// 我们先创建了一个异步并发队列,然后在队列中调用syncMain方法。
dispatch_queue_t queue = dispatch_queue_create("net.test.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
            NSLog(@"当前线程:%@",[NSThread currentThread]);
        [self syncMain];
    });
输出的结果:
2018-09-21 10:52:54.968128+0800 GCDDemo[1926:297771] 当前线程:<NSThread: 0x6000023a23c0>{number = 3, name = (null)}
2018-09-21 10:52:54.976098+0800 GCDDemo[1926:297771] begin-- 主队列 + 同步执行
2018-09-21 10:52:54.978843+0800 GCDDemo[1926:297474] 1------<NSThread: 0x6000023993c0>{number = 1, name = main}
2018-09-21 10:52:54.978939+0800 GCDDemo[1926:297474] 1------<NSThread: 0x6000023993c0>{number = 1, name = main}
2018-09-21 10:52:54.979112+0800 GCDDemo[1926:297474] 2------<NSThread: 0x6000023993c0>{number = 1, name = main}
2018-09-21 10:52:54.979187+0800 GCDDemo[1926:297474] 2------<NSThread: 0x6000023993c0>{number = 1, name = main}
2018-09-21 10:52:54.979789+0800 GCDDemo[1926:297474] 3------<NSThread: 0x6000023993c0>{number = 1, name = main}
2018-09-21 10:52:54.979871+0800 GCDDemo[1926:297474] 3------<NSThread: 0x6000023993c0>{number = 1, name = main}
2018-09-21 10:52:54.979958+0800 GCDDemo[1926:297771] end-- 主队列 + 同步执行

从打印中可以总结如下:

疑问点:为什么在其他线程调用不会崩溃卡住呢?
:因为syncMain任务放到了其他线程里,而syncMain方法中的几个任务都追加到主队列中,因为主队列现在没有正在执行的任务,所以会直接执行主队列的任务,一个个执行下去,所以这里不会卡住线程。

6.主队列 + 异步执行
特点:只在主线程中执行任务,执行完一个任务,再执行下一个任务。

//主队列 + 异步执行
- (void)asyncMain
{
    NSLog(@"begin-- 主队列 + 异步执行");
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"1------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; i++) {
            NSLog(@"3------%@",[NSThread currentThread]);
        }
    });
    NSLog(@"end-- 主队列 + 异步执行");
}

输出的结果:
2018-09-21 11:13:55.685841+0800 GCDDemo[2099:354235] begin-- 主队列 + 异步执行
2018-09-21 11:13:55.686023+0800 GCDDemo[2099:354235] end-- 主队列 + 异步执行
2018-09-21 11:13:55.695339+0800 GCDDemo[2099:354235] 1------<NSThread: 0x600000c653c0>{number = 1, name = main}
2018-09-21 11:13:55.695555+0800 GCDDemo[2099:354235] 1------<NSThread: 0x600000c653c0>{number = 1, name = main}
2018-09-21 11:13:55.695661+0800 GCDDemo[2099:354235] 2------<NSThread: 0x600000c653c0>{number = 1, name = main}
2018-09-21 11:13:55.695733+0800 GCDDemo[2099:354235] 2------<NSThread: 0x600000c653c0>{number = 1, name = main}
2018-09-21 11:13:55.695795+0800 GCDDemo[2099:354235] 3------<NSThread: 0x600000c653c0>{number = 1, name = main}
2018-09-21 11:13:55.695852+0800 GCDDemo[2099:354235] 3------<NSThread: 0x600000c653c0>{number = 1, name = main}

从打印中可以总结如下:

1.所有任务都是在主线程中执行的,并没有开启新的线程,虽然异步执行具备开启线程的能力,但因为是主队列,所以所有任务都在主队列(主队列是串行队列)中,并且一个接一个的执行下去.
2.所有任务是在打印的begin-- 和end-- 之后才开始执行的,说明任务并不是马上执行,而是将所有任务添加到队列之后才开始同步执行。

1.栅栏方法(dispatch_barrier_async和dispatch_barrier_sync)

//dispatch_barrier_async
- (void) asyncbarrier
{
   dispatch_queue_t queue = dispatch_queue_create("net.test.queue", DISPATCH_QUEUE_CONCURRENT);
   dispatch_async(queue, ^{
       NSLog(@"----1-----%@", [NSThread currentThread]);
   });
   dispatch_async(queue, ^{
       NSLog(@"----2-----%@", [NSThread currentThread]);
   });
   dispatch_barrier_async(queue, ^{
       NSLog(@"----barrier-----%@", [NSThread currentThread]);
   });
   NSLog(@"aa, %@", [NSThread currentThread]);
   dispatch_async(queue, ^{
       NSLog(@"----3-----%@", [NSThread currentThread]);
   });
   NSLog(@"bb, %@", [NSThread currentThread]);
   dispatch_async(queue, ^{
       NSLog(@"----4-----%@", [NSThread currentThread]);
   });
}

输出的结果:
2018-09-21 14:36:35.946008+0800 GCDDemo[3665:625310] aa, <NSThread: 0x600002f013c0>{number = 1, name = main}
2018-09-21 14:36:35.946012+0800 GCDDemo[3665:625532] ----1-----<NSThread: 0x600002f57dc0>{number = 3, name = (null)}
2018-09-21 14:36:35.946027+0800 GCDDemo[3665:625531] ----2-----<NSThread: 0x600002f5d3c0>{number = 4, name = (null)}
2018-09-21 14:36:35.946139+0800 GCDDemo[3665:625310] bb, <NSThread: 0x600002f013c0>{number = 1, name = main}
2018-09-21 14:36:35.946161+0800 GCDDemo[3665:625531] ----barrier-----<NSThread: 0x600002f5d3c0>{number = 4, name = (null)}
2018-09-21 14:36:35.946258+0800 GCDDemo[3665:625532] ----4-----<NSThread: 0x600002f57dc0>{number = 3, name = (null)}
2018-09-21 14:36:35.946277+0800 GCDDemo[3665:625531] ----3-----<NSThread: 0x600002f5d3c0>{number = 4, name = (null)}

从打印中可以总结如下:

//dispatch_barrier_sync
- (void) syncbarrier
{
   dispatch_queue_t queue = dispatch_queue_create("net.test.queue", DISPATCH_QUEUE_CONCURRENT);
   dispatch_async(queue, ^{
       NSLog(@"----1-----%@", [NSThread currentThread]);
   });
   dispatch_async(queue, ^{
       NSLog(@"----2-----%@", [NSThread currentThread]);
   });
   dispatch_barrier_sync(queue, ^{
       NSLog(@"----barrier-----%@", [NSThread currentThread]);
   });
   NSLog(@"aa, %@", [NSThread currentThread]);
   dispatch_async(queue, ^{
       NSLog(@"----3-----%@", [NSThread currentThread]);
   });
   NSLog(@"bb, %@", [NSThread currentThread]);
   dispatch_async(queue, ^{
       NSLog(@"----4-----%@", [NSThread currentThread]);
   });
}

输出的结果:
2018-09-21 14:38:45.374566+0800 GCDDemo[3689:634918] ----2-----<NSThread: 0x6000032dfa80>{number = 4, name = (null)}
2018-09-21 14:38:45.374567+0800 GCDDemo[3689:634917] ----1-----<NSThread: 0x6000032d04c0>{number = 3, name = (null)}
2018-09-21 14:38:45.374780+0800 GCDDemo[3689:634674] ----barrier-----<NSThread: 0x60000328d3c0>{number = 1, name = main}
2018-09-21 14:38:45.374858+0800 GCDDemo[3689:634674] aa, <NSThread: 0x60000328d3c0>{number = 1, name = main}
2018-09-21 14:38:45.374939+0800 GCDDemo[3689:634674] bb, <NSThread: 0x60000328d3c0>{number = 1, name = main}
2018-09-21 14:38:45.374956+0800 GCDDemo[3689:634917] ----3-----<NSThread: 0x6000032d04c0>{number = 3, name = (null)}
2018-09-21 14:38:45.375039+0800 GCDDemo[3689:634917] ----4-----<NSThread: 0x6000032d04c0>{number = 3, name = (null)}

从打印中可以总结如下:

注意点:在使用栅栏函数时,需使用自定义队列才有意义,如果用的是串行队列或者系统提供的全局并发队列,这个栅栏函数的作用等同于一个同步函数的作用。

dispatch_barrier_asyncdispatch_barrier_sync异同点总结:

相同点:

不同点:

2.延时执行方法(dispatch_after)

//dispatch_after
- (void)after {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"run-----%@",[NSThread currentThread]);
    });
}

输出的结果:
2018-09-25 14:06:01.709795+0800 GCDDemo[6349:555122] run-----<NSThread: 0x600002046940>{number = 1, name = main}

需要注意的地方:

3.快速迭代方法(dispatch_apply)

//dispatch_apply
- (void)apply {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"begin-- ");
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"%zd---%@",index, [NSThread currentThread]);
    });
    NSLog(@"end--");
}

输出的结果:
2018-09-25 14:23:48.028596+0800 GCDDemo[6495:604219] begin--
2018-09-25 14:23:48.028858+0800 GCDDemo[6495:604219] 0---<NSThread: 0x6000003613c0>{number = 1, name = main}
2018-09-25 14:23:48.028874+0800 GCDDemo[6495:604631] 1---<NSThread: 0x60000032f880>{number = 3, name = (null)}
2018-09-25 14:23:48.028894+0800 GCDDemo[6495:604632] 2---<NSThread: 0x6000003034c0>{number = 4, name = (null)}
2018-09-25 14:23:48.028917+0800 GCDDemo[6495:604630] 3---<NSThread: 0x600000303680>{number = 5, name = (null)}
2018-09-25 14:23:48.028944+0800 GCDDemo[6495:604219] 4---<NSThread: 0x6000003613c0>{number = 1, name = main}
2018-09-25 14:23:48.028961+0800 GCDDemo[6495:604631] 5---<NSThread: 0x60000032f880>{number = 3, name = (null)}
2018-09-25 14:23:48.029002+0800 GCDDemo[6495:604632] 6---<NSThread: 0x6000003034c0>{number = 4, name = (null)}
2018-09-25 14:23:48.029018+0800 GCDDemo[6495:604630] 7---<NSThread: 0x600000303680>{number = 5, name = (null)}
2018-09-25 14:23:48.029059+0800 GCDDemo[6495:604219] 8---<NSThread: 0x6000003613c0>{number = 1, name = main}
2018-09-25 14:23:48.029071+0800 GCDDemo[6495:604631] 9---<NSThread: 0x60000032f880>{number = 3, name = (null)}
2018-09-25 14:23:48.029626+0800 GCDDemo[6495:604219] end--

需要注意的地方:

4.只执行一次 ( dispatch_once)

//dispatch_once
- (void)once {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只执行1次的代码(这里面默认是线程安全的)
    });
}

常用使用场景:单例

1.dispatch_group_notify
特点:监听 所有添加到group 中任务的完成状态,当所有的任务都执行完成后,追加任务到 group 中,并执行任务。

 // dispatch_group_notify
- (void)notify {

   NSLog(@"group---begin");
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(3);
        NSLog(@"----2-----%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(4);
        NSLog(@"----3-----%@", [NSThread currentThread]);
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的异步任务1、任务2、任务3都执行完毕后,回到主线程执行下边任务
        NSLog(@"----4-----%@", [NSThread currentThread]);
        NSLog(@"group---end");
    });
    NSLog(@"----5-----end");
}

输出的结果:
2018-09-25 16:33:18.057450+0800 GCDDemo[7462:922204] group---begin
2018-09-25 16:33:18.057673+0800 GCDDemo[7462:922204] ----5-----end
2018-09-25 16:33:18.057726+0800 GCDDemo[7462:922320] ----1-----<NSThread: 0x60000196a540>{number = 3, name = (null)}
2018-09-25 16:33:21.062976+0800 GCDDemo[7462:922311] ----2-----<NSThread: 0x6000019073c0>{number = 4, name = (null)}
2018-09-25 16:33:22.058039+0800 GCDDemo[7462:922308] ----3-----<NSThread: 0x600001921140>{number = 5, name = (null)}
2018-09-25 16:33:22.058223+0800 GCDDemo[7462:922204] ----4-----<NSThread: 0x600001961280>{number = 1, name = main}
2018-09-25 16:33:22.058290+0800 GCDDemo[7462:922204] group---end

从打印中可以总结如下:

2.dispatch_group_wait
特点:等待group关联的block执行完毕,才会继续往下执行。

 //dispatch_group_wait
- (void)wait {

    NSLog(@"group---begin");
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(2);
        NSLog(@"----1-----%@", [NSThread currentThread]);
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
    });
    // 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"group---end");
}

输出的结果:
2018-09-26 10:24:51.163832+0800 GCDDemo[1907:215711] group---begin
2018-09-26 10:24:51.164102+0800 GCDDemo[1907:215790] ----2-----<NSThread: 0x600003fc0700>{number = 3, name = (null)}
2018-09-26 10:24:51.164118+0800 GCDDemo[1907:215793] ----3-----<NSThread: 0x600003f9b180>{number = 4, name = (null)}
2018-09-26 10:24:53.164214+0800 GCDDemo[1907:215791] ----1-----<NSThread: 0x600003fed880>{number = 5, name = (null)}
2018-09-26 10:24:53.164357+0800 GCDDemo[1907:215711] group---end

从打印中可以总结如下:

3.dispatch_group_enter和dispatch_group_leave

特点:``dispatch_group_enterdispatch_group_leave总是成对出现的。
dispatch_group_enter:用于添加对应任务组中的未执行完毕的任务数,执行一次,未执行完毕的任务数加1,当未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞和dispatch_group_notify的block执行。
dispatch_group_leave:用于减少任务组中的未执行完毕的任务数,执行一次,未执行完毕的任务数减1,dispatch_group_enterdispatch_group_leave要匹配,不然系统会认为group任务没有执行完毕。

//dispatch_group_enter、dispatch_group_leave
- (void)enterAndLeave{

    NSLog(@"group---begin");
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
        NSLog(@"group---end");
    });

输出的结果:
2018-09-26 11:13:05.879685+0800 GCDDemo[2589:360180] group---begin
2018-09-26 11:13:05.880582+0800 GCDDemo[2589:360271] ----2-----<NSThread: 0x6000016580c0>{number = 4, name = (null)}
2018-09-26 11:13:05.880934+0800 GCDDemo[2589:360272] ----1-----<NSThread: 0x60000166a240>{number = 3, name = (null)}
2018-09-26 11:13:05.889769+0800 GCDDemo[2589:360180] ----3-----<NSThread: 0x600001634c00>{number = 1, name = main}
2018-09-26 11:13:05.889853+0800 GCDDemo[2589:360180] group---end
}
    dispatch_samaphore_t dispatch_semaphore_create(long value);

传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量。
值得注意的是,这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL
2.dispatch_semaphore_signal函数的声明为:

     long dispatch_semaphore_signal(dispatch_semaphore_tdsema)

这个函数会使传入的信号量dsema的值加1
3. dispatch_semaphore_wait函数的声明为:

   long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

这个函数会使传入的信号量dsema的值减1
这个函数的作用是这样的,如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;如果desema的值为0,那么这个函数就阻塞当前线程等待timeout(注意timeout的类型为dispatch_time_t,不能直接传入整型float类型),如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1。如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句。
4.dispatch_semaphore_signal和dispatch_semaphore_wait返回值代表的意义(这里要注意函数返回值和dsema的值的区别):
dispatch_semaphore_signal当返回值为0时表示当前并没有线程等待其处理的信号量,其处理的信号量的值加1即可。当返回值不为0时,表示其当前有(一个或多个)线程等待其处理的信号量,并且该函数唤醒了一个等待的线程(当线程有优先级时,唤醒优先级最高的线程;否则随机唤醒)。
dispatch_semaphore_wait的返回值也为long类型。当其返回0时表示在timeout之前,该函数所处的线程被成功唤醒,当其返回不为0时,表示timeout发生。
5.设置timeout
比较有用的两个宏:DISPATCH_TIME_NOWDISPATCH_TIME_FOREVER,当然你也可以自己创建一个dispatch_time_t类型的变量来使用。
例如:dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 1*1000*1000*1000);
6.关于信号量通俗的解释:
停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。
信号量的值就相当于剩余车位的数目,dispatch_semaphore_wait函数就相当于来了一辆车,dispatch_semaphore_signal就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)),调用一次dispatch_semaphore_signal,剩余的车位就增加一个;调用一次dispatch_semaphore_wait剩余车位就减少一个;当剩余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等待。有可能同时有几辆车等待一个停车位。有些车主没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在这,所以就一直等下去。

-(void)dispatchSignal{
    //crate的value表示,最多几个资源可访问
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);   
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     

    dispatch_async(quene, ^{
        //任务1
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"----1-----");
        sleep(1);
        NSLog(@"complete ----1-----");
        dispatch_semaphore_signal(semaphore);       
    });
  
    dispatch_async(quene, ^{
         //任务2
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"----2-----");
        sleep(1);
        NSLog(@"complete ----2-----");
        dispatch_semaphore_signal(semaphore);       
    });
    
    dispatch_async(quene, ^{
         //任务3
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"----3-----");
        sleep(1);
        NSLog(@"complete ----3-----");
        dispatch_semaphore_signal(semaphore);       
    });   
}

信号量的初始值设置为1时输出结果:
2018-09-26 16:54:52.756489+0800 GCDDemo[7192:1007207] ----1-----
2018-09-26 16:54:53.759805+0800 GCDDemo[7192:1007207] complete ----1-----
2018-09-26 16:54:53.759963+0800 GCDDemo[7192:1007211] ----2-----
2018-09-26 16:54:54.763736+0800 GCDDemo[7192:1007211] complete ----2-----
2018-09-26 16:54:54.763988+0800 GCDDemo[7192:1007209] ----3-----
2018-09-26 16:54:55.769148+0800 GCDDemo[7192:1007209] complete ----3-----

信号量的初始值设置为2时输出结果:
2018-09-26 16:56:10.535546+0800 GCDDemo[7231:1012058] ----1-----
2018-09-26 16:56:10.535565+0800 GCDDemo[7231:1012060] ----2-----
2018-09-26 16:56:11.540570+0800 GCDDemo[7231:1012060] complete ----2-----
2018-09-26 16:56:11.540585+0800 GCDDemo[7231:1012058] complete ----1-----
2018-09-26 16:56:11.540705+0800 GCDDemo[7231:1012057] ----3-----
2018-09-26 16:56:12.541442+0800 GCDDemo[7231:1012057] complete ----3-----

信号量的初始值设置为3时输出结果:
2018-09-26 16:56:43.973919+0800 GCDDemo[7248:1014760] ----1-----
2018-09-26 16:56:43.973922+0800 GCDDemo[7248:1014763] ----2-----
2018-09-26 16:56:43.973923+0800 GCDDemo[7248:1014761] ----3-----
2018-09-26 16:56:44.974419+0800 GCDDemo[7248:1014763] complete ----2-----
2018-09-26 16:56:44.974419+0800 GCDDemo[7248:1014761] complete ----3-----
2018-09-26 16:56:44.974451+0800 GCDDemo[7248:1014760] complete ----1-----

从打印中可以总结如下:

注意点: dispatch_semaphore_waitdispatch_semaphore_signal成对出现,否则程序会崩溃。

GCD的常见面试题

一.输出题(自行检测)

- (void)viewDidLoad {

    [super viewDidLoad];

       NSLog(@"1");
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"2");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
    while (1) {
    };

}
- (void)viewDidLoad {

    [super viewDidLoad];

    dispatch_queue_t queue = dispatch_queue_create("come.test.serialQueue", DISPATCH_QUEUE_SERIAL);

       NSLog(@"1");
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
    while (1) {
    };

}
- (void)viewDidLoad {

    [super viewDidLoad];

       NSLog(@"1");
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"2");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");

}
- (void)viewDidLoad {

    [super viewDidLoad];

    dispatch_queue_t queue = dispatch_queue_create("come.test.serialQueue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");

}

二.解答题
1、 dispatch_group_async是否一定能实现线程同步?
2、dispatch_barrier_syncdispatch_barrier_async的区别和相同点有哪些?
3、dispatch_group_asyncdispatch_group_enter,dispatch_group_leave实现线程同步有什么区别?

实际使用中的一些例子

写在后面的话

有问题的地方,请多指教,这个主要供自己复习和学习使用,温故知新~

上一篇 下一篇

猜你喜欢

热点阅读