GCD(Grand Central Dispatch) 是基于C语言的API,是苹果公司为多核的并行运算提出的解决方案。GCD会自动利用更多的CPU内核(好比双核、四核)。程序员只须要将任务添加到队列中,而且指定执行任务的函数,不须要别写任何线程管理的代码。程序员
学习 GCD 以前,先来了解 GCD 中两个核心概念:任务
和 队列
。安全
任务
:就是执行操做的意思,通俗的说就是你在线程中执行的那段代码。在 GCD 中是放在 block 中的。bash
执行任务有两种方式:同步执行
和 异步执行
。二者的主要区别是:是否等待队列的任务执行结束,以及是否具有开启新线程的能力
。网络
dispatch_sync
等待
当前队列的任务执行完毕,才能执行下一个任务dispatch_async
等待
,能够继续执行任务。队列(Dispatch Queue)
:这里的队列指执行任务的等待队列,即用来存听任务的队列。队列是一种特殊的线性表,采用 FIFO(First - In -First - Out)
的原则,即新任务老是被插入到队列的末尾,而读取任务的时候老是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。队列的结构可参考下图:多线程
在 GCD 中有两种队列:串行队列
和 并发队列
。二者都符合 FIFO
原则。二者的主要区别是:执行顺序不一样,以及开启线程数不一样
。并发
Serial Dispatch Queue
每次只有一个 任务
被执行,只申请一个 线程
,一个 任务
执行完毕,再执行下一个Concurrent Dispatch Queue
能够申请多个
线程
,让多个
任务
同时执行(注意:并发队列的并发功能只有在 异步dispatch_async 方法下有效)
GCD使用很简单,只有三步app
任务
(同步任务 或者 异步任务)队列
(串行队列 或者 并发队列)任务
追加到任务的等待 队列
中,而后系统就会根据任务类型执行任务(同步执行 或者 异步执行)同步执行任务的建立方法 dispatch_sync
和 异步执行任务的建立方法 dispatch_async
。异步
// 同步任务建立方法
dispatch_sync(queue, ^{
// 这里放同步任务代码
});
// 异步执行任务建立方法
dispatch_async(queue, ^{
// 这里放异步任务代码
});
复制代码
GCD 可使用 dispatch_queue_creat
建立队列async
dispatch_queue_t queue - dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>);
复制代码
const char * _Nullable label
: 队列的惟一标识符,用于debug,能够为空。dispatch_queue_attr_t _Nullable attr
: 队列种类。DISPATCH_QUEUE_SERIAL
串行队列,DISPATCH_QUEUE_CONCURRENT
并发队列。第二个参数若是传NULL,会建立串行队列。ide
对于串行队列,GCD默认提供了:主队列 - Main Dispatch Queue
dispatch_get_main_queue()
方法得到主队列主队列本质就是一个普通的串行队列,只是默认状况下,代码放在主队列中,主队列的代码会放到主程序中执行,给咱们主队列特殊的感受。
对于并发队列,GCD默认提供了:全局并发队列 - Global Dispatch Queue
。可使用 dispatch_get_global_queue()
方法得到全局并发队列
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
dispatch_queue_t queue = dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>)
复制代码
long identifier
: 标示队列优先级,通常用 DISPATCH_QUEUE_PRIORITY_DEFAULT
- (void)syncSerial
{
NSLog(@"主线程 - %@", [NSThread currentThread]);
NSLog(@"---begin---");
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_SERIAL);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_sync(queue, ^{
NSLog(@"同步任务 + 串行队列:%ld -- %@", (long)i, [NSThread currentThread]);
});
}
NSLog(@"---end---");
}
复制代码
同步任务只能在当前线程中执行任务,不具有开启新线程的能力
)同步任务须要等待队列的任务执行结束
)串行队列每次只有一个任务被执行,任务一个接一个按顺序执行
)。- (void)asyncSerial
{
NSLog(@"主线程 - %@", [NSThread currentThread]);
NSLog(@"---begin---");
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_SERIAL);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"异步任务 + 串行队列:%ld -- %@", (long)i, [NSThread currentThread]);
});
}
NSLog(@"---end---");
}
复制代码
异步任务具备开辟线程的能力,串行队列只开启一个线程
)异步执行不会作任何等待,能够继续执行接下来的任务
)串行队列每次只有一个任务被执行,任务一个接一个按顺序执行
)- (void)syncConcurrent
{
NSLog(@"主线程 - %@", [NSThread currentThread]);
NSLog(@"---begin---");
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_sync(queue, ^{
NSLog(@"同步任务 + 并行队列:%ld -- %@", (long)i, [NSThread currentThread]);
});
}
NSLog(@"---end---");
}
复制代码
同步任务只能在当前线程中执行任务,不具有开启新线程的能力
)同步任务须要等待队列的任务执行结束
)并发队列
能够开启多个线程,而且同时执行多个任务。可是由于自己不能建立新线程,只有当前线程这一个线程(同步任务不具有开启新线程的能力
),因此也就不存在并发。并且当前线程只有等待当前队列中正在执行的任务执行完毕以后,才能继续接着执行下面的操做(同步任务 须要等待队列的任务执行结束
)。因此任务只能一个接一个按顺序执行,不能同时被执行。- (void)asyncConcurrent
{
NSLog(@"主线程 - %@", [NSThread currentThread]);
NSLog(@"---begin---");
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_async(queue, ^{
NSLog(@"异步任务 + 并行队列:%ld -- %@", (long)i, [NSThread currentThread]);
});
}
NSLog(@"---end---");
}
复制代码
异步任务具有开启新线程的能力。 并发队列 可开启多个线程,同时执行多个任务
)。异步执行不会作任何等待,能够继续执行接下来的任务
)同步任务 + 主队列
在不一样线程中调用结果也是不同,在主线程中调用会发生死锁问题,而在其余线程中调用则不会。
同步任务 + 主队列
崩溃缘由:主队列是串行队列 全部放在主队列中的任务,都会放到主线程中执行
, 当咱们在主线程
中执行syncMain方法,至关于把 syncMain任务
放到了 主线程
的队列中。而 同步执行会等待当前队列中的任务执行完毕,才会接着执行
。那么当咱们把 任务1
追加到主队列中,任务1
就在等待主线程处理完 syncMain任务
。而 syncMain任务
须要等待 任务1
执行完毕,才能接着执行。两个任务 相互等待
对方执行完毕。
同步任务 + 主队列
// 使用 NSThread 的 detachNewThreadSelector 方法会建立线程,并自动启动线程执行 selector 任务
[NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];
复制代码
全部放在主队列中的任务,都会放到主线程中执行
) 全部打印都在 ---begin--- 和 ---end--- 之间执行(同步任务须要等待队列的任务执行结束
)主队列是 串行队列,每次只有一个任务被执行,任务一个接一个按顺序执行
)为何如今就不会卡住了呢?
由于 syncMain任务
放到 子线程
里; 而 任务1到10
都在追加到主队列中,会在 主线程
中执行。主队列如今没有正在执行的任务,因此会直接执行主队列的 任务1
,等 任务1
执行完毕,再接着执行 任务2
。因此这里不会卡住线程,也就不会形成死锁问题。
如下demo默认都在主队列,由于主队列是串行队列,代码自上到下依次执行。默认异步操做都是长耗时操做。
- (void)demo1
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_async(queue, ^{
sleep(2);
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
打印顺序:1 5 2 死锁
复制代码
代码块B
为同步任务,等待,产生阻塞。因此,任务4
要等到代码块B
执行完毕才能执行。代码块B
要执行完毕,必须等到 任务3
执行完毕.任务3
要等到 任务4
执行完毕才执行任务4
等块B
,块B
等 任务3
,任务3
等 任务4
。死锁。- (void)demo2
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_sync(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
sleep(2);
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
打印顺序: 12453
复制代码
任务1 代码块A 任务5
。代码块A
是同步任务,等待。 因此打印 1 代码块A 5代码块A
内: 队列中依次加入 任务2 代码块B 任务4
。代码块B
是异步任务,不等待,耗时。因此打印 2 4 3代码块B
执行完毕后 打印5- (void)demo3
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(queue, ^{
sleep(2);
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
打印结果:15234
复制代码
任务1 代码块A 任务5
。代码块A
是异步任务,不等待,耗时。因此打印 1 5代码块A
内:并行队列,申请新线程。新线程依次加入任务2 代码块B 任务4
。代码块B
同步任务,等待阻塞当前子线程。打印 2 3 4- (void)demo4
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_sync(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
sleep(2);
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
打印结果:12453
复制代码
任务1 代码块A 任务5
。代码块A
是同步任务,等待。因此打印 1 代码块A 5代码块A
内:并行队列,加入 任务2 代码块B 任务4
。代码块B
异步任务,不等待,耗时,申请新的线程。应用场景:在指定时间以后执行某个任务。
dispatch_after
方法并非在指定时间以后才开始执行处理,而是在指定时间以后将任务追加到主队列中。严格来讲,这个时间并非绝对准确的,但想要大体延迟执行任务,dispatch_after
方法是颇有效的。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2秒后输出");
});
复制代码
应用场景:单例,method-Swizzling
dispatch_once
能保证某段代码在程序运行过程当中只被执行 1 次,而且即便在多线程的环境下,dispatch_once
也能够保证线程安全。
- (void)once {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行 1 次的代码(这里面默认是线程安全的)
});
}
复制代码
应用场景:得到网络数据后提早算出各个控件的大小
dispatch_apply 按照指定的次数将指定的任务追加到指定的队列中,并等待所有队列执行结束。
- (void)apply
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_SERIAL);
NSLog(@"dispatch_apply前");
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"dispatch_apply -- 线程%zu-%@", index, [NSThread currentThread]);
});
NSLog(@"dispatch_apply后");
}
复制代码
同步操做
,也像是队列组中的
dispatch_group_wait
方法。
应用场景:线程同步
异步任务+并发队列会开辟线程,多个任务同时进行,各任务也会由于任务复杂度和cpu的调度致使执行完毕乱序。如何设置让任务顺序执行完毕,栅栏函数就是很好的解决办法。
dispatch_barrier_async
用于提交异步执行栅栏函数块任务,并当即返回。该函数不会阻塞线程
,只会阻塞任务
执行。栅栏函数提交以后并不会当即执行,而是会直接返回。等待在栅栏函数以前提交的任务都执行完成以后,栅栏函数任务会自动执行,在栅栏函数以后提交的任务必须在栅栏函数执行完成以后才会执行.
dispatch_barrier_sync
用于提交同步执行栅栏函数块任务,并不会当即返回,须要等待栅栏函数提交的任务执行完毕以后才会返回。与dispatch_barrier_async不一样,因为该函数须要同步执行,在该栅栏函数任务执行完成以前,函数不会返回,因此此时后边的任务都会被阻塞
.
- (void)barrierSync
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"任务1 -- %@", [NSThread currentThread]);
});
dispatch_barrier_sync(queue, ^{
NSLog(@"dispatch_barrier_sync -- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2 -- %@", [NSThread currentThread]);
});
NSLog(@"end -- %@", [NSThread currentThread]);
}
复制代码
- (void)barrierAsync
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"任务1 -- %@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async -- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2 -- %@", [NSThread currentThread]);
});
NSLog(@"end -- %@", [NSThread currentThread]);
}
复制代码
测试1
和
测试2
:
dispatch_barrier_sync
与 dispatch_barrier_async
的共同点:任务begin
任务1
)先执行完。barrier operation
)执行完以后再执行后面的任务(任务2
任务end
)。dispatch_barrier_sync
与 dispatch_barrier_async
的不一样点:dispatch_barrier_sync
须要等待本身的任务(barrier operation
)执行完毕后,才会插入后面的任务(任务2
任务end
),而后执行后面的任务。因此 任务end
比 barrier operation
后打印。说白了,会阻塞线程。dispatch_barrier_async
将本身的任务插入到队列以后,不会等待本身的任务(barrier operation)执行结果,它会继续插入后面的任务。因此 任务end
先打印。dispatch_barrier_sync线程
与 任务线程
不一致,会怎样?- (void)barrierSync
{
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
// 新建线程
dispatch_queue_t queue2 = dispatch_queue_create("com.akironer2", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"任务1 -- %@", [NSThread currentThread]);
});
// 阻塞新建线程
dispatch_barrier_sync(queue2, ^{
NSLog(@"dispatch_barrier_sync -- %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2 -- %@", [NSThread currentThread]);
});
NSLog(@"end -- %@", [NSThread currentThread]);
}
复制代码
dispatch_barrier_async
测试,也有一样的结果
dispatch_barrier 线程
与 任务线程
不一致,barrier没有发挥做用。全局并发队列
使用栅栏函数,有什么效果?应用场景:分别异步执行2个耗时任务,而后当2个耗时任务都执行完毕后再回到主线程执行任务。
dispatch_group_async
先把任务放到队列中,而后将队列放入队列组中dispatch_group_notify
监听 group 中任务的完成状态,当全部的任务都执行完成后,追加任务
到 group
中,并执行任务。- (void)groupAsync
{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_group_async(group, queue, ^{
sleep(5);
NSLog(@"任务1 -- %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
sleep(3);
NSLog(@"任务2 -- %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
sleep(4);
NSLog(@"任务3 -- %@", [NSThread currentThread]);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"end -- %@", [NSThread currentThread]);
});
}
打印:
22:23:12.180761+0800 GCD[2123:330417] begin -- <NSThread: 0x60000212ad40>{number = 1, name = main}
22:23:15.182107+0800 GCD[2123:330584] 任务2 -- <NSThread: 0x600002150080>{number = 3, name = (null)}
22:23:16.180985+0800 GCD[2123:330585] 任务3 -- <NSThread: 0x60000215c180>{number = 4, name = (null)}
22:23:17.182406+0800 GCD[2123:330596] 任务1 -- <NSThread: 0x600002122ec0>{number = 5, name = (null)}
22:23:17.182667+0800 GCD[2123:330596] end -- <NSThread: 0x600002122ec0>{number = 5, name = (null)}
复制代码
暂停当前线程,等待指定的group中的任务执行完成以后,才往下执行任务。相比于dispatch_group_notify
, dispatch_group_wait
会阻塞线程。
- (void)groupWait
{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_group_async(group, queue, ^{
sleep(5);
NSLog(@"任务1 -- %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
sleep(3);
NSLog(@"任务2 -- %@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
sleep(4);
NSLog(@"任务3 -- %@", [NSThread currentThread]);
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"end -- %@", [NSThread currentThread]);
}
打印:
22:31:45.390633+0800 GCD[2157:336097] begin -- <NSThread: 0x6000001d6d00>{number = 1, name = main}
22:31:48.392504+0800 GCD[2157:336257] 任务2 -- <NSThread: 0x6000001a4640>{number = 6, name = (null)}
22:31:49.392455+0800 GCD[2157:336255] 任务3 -- <NSThread: 0x6000001dc400>{number = 5, name = (null)}
22:31:50.395577+0800 GCD[2157:336256] 任务1 -- <NSThread: 0x6000001ac380>{number = 4, name = (null)}
22:31:50.395978+0800 GCD[2157:336256] end -- <NSThread: 0x6000001ac380>{number = 4, name = (null)}
复制代码
dispatch_group_enter
标志着一个任务追加到 group,执行一次,至关于 group 中未执行完毕任务数 +1dispatch_group_leave
标志着一个任务离开了 group,执行一次,至关于 group 中未执行完毕任务数 -1。dispatch_group_wait
解除阻塞,以及执行追加到 dispatch_group_notify
中的任务。- (void)groupWait
{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"begin -- %@", [NSThread currentThread]);
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
sleep(5);
NSLog(@"任务1 -- %@", [NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
sleep(3);
NSLog(@"任务2 -- %@", [NSThread currentThread]);
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"end -- %@", [NSThread currentThread]);
});
}
打印:
22:42:10.338809+0800 GCD[2188:343124] begin -- <NSThread: 0x60000338adc0>{number = 1, name = main}
22:42:13.340545+0800 GCD[2188:343288] 任务2 -- <NSThread: 0x6000033e8340>{number = 5, name = (null)}
22:42:15.343788+0800 GCD[2188:343293] 任务1 -- <NSThread: 0x600003304000>{number = 7, name = (null)}
22:42:15.344152+0800 GCD[2188:343293] end -- <NSThread: 0x600003304000>{number = 7, name = (null)}
复制代码
应用场景:
信号量是基于计数器的一种多线程同步机制,用来管理对资源的并发访问。信号量内部有一个能够原子递增或递减的值。若是一个动做尝试减小信号量的值,使其小于0,那么这个动做将会被阻塞,直到有其余调用者(在其余线程中)增长该信号量的值。
dispatch_semaphore_create
:建立一个 Semaphore 并初始化信号的总量dispatch_semaphore_signal
:发送一个信号,让信号总量加 1dispatch_semaphore_wait
:可使总信号量减 1。信号量小于等于 0 则会阻塞当前线程,直到信号量大于0或者通过输入的时间值;若信号量大于0,则会使信号量减1并返回,程序继续住下执行。在 dispatch_semaphore_wait
和 dispatch_semaphore_signal
这两个函数中间的执行代码,每次只会容许限定数量的线程进入,这样就有效的保证了在多线程环境下,只能有限定数量的线程进入。
注意:信号量的使用前提是:想清楚你须要处理哪一个线程等待(阻塞),又要哪一个线程继续执行,而后使用信号量。
- (void)semaphoreAync
{
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_queue_create("com.akironer", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
NSLog(@"%d -- 线程%@", i, [NSThread currentThread]);
// 打印任务结束后信号量解锁
dispatch_semaphore_signal(sem);
});
// 异步耗时,因此这里信号量加锁
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}
}
复制代码
任务1
追加到队列以后,不作等待,接着执行 dispatch_semaphore_wait
,semaphore 减 1,此时 semaphore == -1,当前线程进入阻塞状态。任务1
开始执行。执行到 dispatch_semaphore_signal
以后,总信号量加 1,此时 semaphore == 0,正在被阻塞的线程恢复继续执行。- (void)saleTickets
{
NSLog(@"begin -- %@", [NSThread currentThread]);
self.ticketCount = 20;
// queue1 表明售卖窗口1
dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
// queue2 表明售卖窗口2
dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
[weakSelf saleTicketsSemaphore];
});
dispatch_async(queue2, ^{
[weakSelf saleTicketsSemaphore];
});
}
- (void)saleTicketsSemaphore
{
while (1) {
// 至关于加锁
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
if (self.ticketCount > 0) {
self.ticketCount --;
NSLog(@"剩余票数:%ld 窗口:%@", (long)self.ticketCount, [NSThread currentThread]);
sleep(0.1);
} else {
NSLog(@"卖完了%@", [NSThread currentThread]);
// 至关于解锁
dispatch_semaphore_signal(self.semaphore);
break;
}
// 至关于解锁
dispatch_semaphore_signal(self.semaphore);
}
}
复制代码
- (void)dispatchAsyncLimit
{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
//任务1
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"任务1执行--%@", [NSThread currentThread]);
sleep(1);
NSLog(@"任务1完成--%@", [NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
//任务2
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"任务2执行--%@", [NSThread currentThread]);
sleep(1);
NSLog(@"任务2完成--%@", [NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
//任务3
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"任务3执行--%@", [NSThread currentThread]);
sleep(1);
NSLog(@"任务3完成--%@", [NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
}

复制代码
应用场景:GCDTimer
NSTimer是依赖Runloop的,而Runloop能够运行在不一样的模式下。若是Runloop在阻塞状态,NSTimer触发时间就会推迟到下一个Runloop周期,所以NSTimer在计时上会有偏差。而GCD定时器不依赖Runloop,计时精度要高不少。
// 强持有
@property (nonatomic, strong) dispatch_source_t timer;
// 1.建立队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.建立timer
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 3.设置timer首次执行时间,间隔,精确度
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
// 4.设置timer事件回调
dispatch_source_set_event_handler(_timer, ^{
NSLog(@"GCDTimer");
});
// 5.默认是挂起状态,须要手动激活
dispatch_resume(_timer);
复制代码