1. GCD 简介
-
什么是GCD?
全称是 Grand Central Dispatch 纯 C 语言,提供了非常多强大的函数
-
GCD的优势
GCD 是苹果公司为多核的并行运算提出的解决方案 GCD 会自动利用更多的CPU内核(比如双核、四核) GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程) 程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码
利用函数把任务放入队列
2. 函数和队列
-
线程同步,线程异步 和 串行队列,并发队列
-
线程同步, @synchronized 多个线程同时访问会锁住,每次只允许执行一个任务,所以同步串行和同步并发都会造成堵塞。
-
线程异步,不堵塞当前线程。
-
串行队列:线程FIFO,排队
-
并发队列:多个任务可以同时执行,
异步并发
。 -
异步并发才会使每个正在执行的任务都开辟一条新的线程
-
异步串行只开辟了一条线程number = 6(主线程number = 1).
2.1函数:
异步 `dispatch_async` - 不用等待当前语句执行完毕,就可以执行下一条语句 - 会开启线程执行 block 的任务 - 异步是多线程的代名词 同步 `dispatch_sync` - 必须等待当前语句执行完毕,才会执行下一条语句 - 不会开启线程 - 在当前执行 block 的任务
注意:异步执行(async)虽然具有开启新线程的能力,但是并不一定开启新线程。这跟任务所指定的队列类型有关。 具体例子: github.com/tanghaitao/…
个人理解,队列和线程
类似于 银行呼叫系统
和号码
, 任务相当于处理事情
,怎么处理是银行呼叫系统控制的
,可以同时处理多个,也可以每次处理一个,甚至一会处理一个一会处理多个
,具体处理事情是通过取的号码来的,叫到谁,谁就去窗口
。
2.2 队列:
串行
并发
- (void)syncTest{
//1:创建串行队列
dispatch_queue_t queue = dispatch_queue_create("Cooci", DISPATCH_QUEUE_SERIAL);
//下面的方式也可以,但是用得少, DISPATCH_QUEUE_SERIAL 更加易懂
//dispatch_queue_t queue = dispatch_queue_create("Cooci", NULL);
//2:创建任务
dispatch_block_t taskBlock = ^{
NSLog(@"%@",[NSThread currentThread]);
};
//3:利用函数把任务放入队列
dispatch_sync(queue, taskBlock);
}
2.2.1 串行同步队列:
串行同步队列 : FIFO: 先进先出
/**
串行同步队列 : FIFO: 先进先出
*/
- (void)serialSyncTest{
//1:创建串行队列
dispatch_queue_t queue = dispatch_queue_create("Cooci", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i<20; i++) {
dispatch_sync(queue, ^{
NSLog(@"%d-%@",i,[NSThread currentThread]);//1-<NSThread: 0x6000001e4900>{number = 1, name = main}
});
}
}
2.2.2 串行异步队列:
串行异步队列
*/
- (void)serialAsyncTest{
//1:创建串行队列
NSLog(@" %@",[NSThread currentThread]);//<NSThread: 0x6000024d41c0>{number = 1, name = main}
dispatch_queue_t queue = dispatch_queue_create("Cooci", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i<20; i++) {
dispatch_async(queue, ^{
NSLog(@"%d-%@",i,[NSThread currentThread]);//{number = 6, name = (null)}
});
}
for (int i = 0; i<1000000; i++) {
}
NSLog(@"hello queue");
}
只开辟了一条线程number = 6(主线程number = 1).
2.2.3 同步并发队列:
堵塞直到之前的任务执行完成,因为是同一个队列同一个线程
/**
同步并发 : 堵塞 同步锁 队列 : resume supend 线程 操作, 队列挂起 任务能否执行
*/
- (void)concurrentSyncTest{
//1:创建并发队列
dispatch_queue_t queue = dispatch_queue_create("Cooci", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<20; i++) {
dispatch_sync(queue, ^{
NSLog(@"%d-%@",i,[NSThread currentThread]);
});
}
for (int i = 0; i<1000000; i++) {
}
NSLog(@"hello queue");
}
2.2.4 异步并发队列
异步并发: 有了异步函数不一定每个任务都会开辟线程,要异步并发才会,异步串行,只会开辟一条线程。
/**
异步并发: 有了异步函数不一定每个任务都会开辟线程,要异步并发才会,异步串行,错的,同一个任务中使用一个新开辟的线程name = (null)
*/
- (void)concurrentAsyncTest{
//1:创建并发队列
dispatch_queue_t queue = dispatch_queue_create("Cooci", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i<20; i++) {
dispatch_async(queue, ^{
NSLog(@"%d-%@",i,[NSThread currentThread]);
// 0-<NSThread: 0x600003dc5dc0>{number = 6, name = (null)}
// 1-<NSThread: 0x600003dc1000>{number = 5, name = (null)}
// 5-<NSThread: 0x600003d98700>{number = 4, name = (null)}
// 2-<NSThread: 0x600003d9cbc0>{number = 3, name = (null)}
});
}
// for (int i = 0; i<1000000; i++) {
//
// }
NSLog(@"hello queue");
}
3. 函数与队列组合应用
下面代码:异步会开线程,消耗内存,执行会滞后
- (void)textDemo{
dispatch_queue_t queue = dispatch_queue_create("haitao", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
// 1 5 2 4 3
}
下面代码:同步阻塞当前线程和队列。
- (void)textDemo1{
dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
// 1 5 2 3 4
}
下面代码:同步阻塞当前线程,串行会FIFO,先进先出,
- (void)textDemo2{
// 同步队列
dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
// 异步函数
dispatch_async(queue, ^{
NSLog(@"2");
// 同步
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
// 1 5 2
}
队列串行,nslog(‘2’),block,4,3
sync. nslog('2'),block,3,4
死锁
在viewDidLoad中执行下面的代码是会产生死锁。队列引起的循环等待
dispatch_sync(dispatch_get_main_queue(),^{
[self doSomething];
});
在主队列中提交了两个任务,一个是viewdidload,一个是block任务,最后分配到主线程执行。当block中的方法调用完成之后,这个viewdidload方法中的执行才会向下走,因为先进队列的是viewdidload,viewdidload中又调用了block,所以block又进入了队列,viewdidload要执行完,需要先执行完block,block执行完需要等队列中viewdidload执行完,所以相互等待的死锁。
主队列serial
专门用来在主线程上调度任务的队列,类似于串行队列
不会开启线程
如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度
dispatch_get_main_queue();
全局队列concurrent
为了方便程序员的使用,苹果提供了全局队列 dispatch_get_global_queue(0, 0)
全局队列是一个并发队列
在使用多线程开发时,如果对队列没有特殊需求,在执行异步任务时,可以直接使用全局队列
__block int a = 0;
// dispatch_group_t t = dispatch_get_main_queue();
// dispatch_get_global_queue : 并发队列
while (a<5) { // 耗时足够长 --- 开辟线程能够调度回来 a++ 线程不安全
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@===%d",[NSThread currentThread],a);
a++;
});
}
此时变量a是线程不安全的,如下图所示,===90,并不是5. 主线程上a也是线程不安全的,每次结果不是固定不变的数据
{number = 1, name = main}****7
{number = 1, name = main}****5
while循环内部调用 异步并发时,会开辟新的线程,
一开始a=0,进入异步并发队列后,a++,但是由于是一次执行了5个全局并发队列,这样会导致每次执行任务的时候在不同的线程中间做调度,来回重复执行多次
,,导致a++执行了N次。
第一次a=0,a++,打印==1,发现第二个线程还没执行a++,又继续打印==1,然后某个线程a++执行完,此时另外一条线程也执行a++,打印==3,多个异步并发队列同时执行同一个任务就会产生很多问题
,类似于同一任务需要不同部门(异步并发队列)同时调度。导致很多重复性的工作
。
解决线程不安全的办法
:一个异步并发队列执行多个相同的任务
,异步并发内部加入while循环但是这样的只能在一条线程中执行(number = 3),效率低。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (a<5) {//声明在里面才正常
NSLog(@"%@===%d",[NSThread currentThread],a);
a++;
}
});
4. GCD应用栅栏函数
4.1 背景:
在做网络请求时,我们会遇到一种情况,如下图所示,需要先请求完token
,再执行其他的网络请求
,导致网络请求 嵌套,如果逻辑很复杂,就会导致阅读性差,不利于维护。
异步转同步,保证线程安全,如果只在一条线程中执行,如上面代码的while循环放到同一队列的情况,一次只能执行一个任务,Sync堵塞,效率就很低,同时无法监听任务执行完后的操作。
[self requestToken:^(id value) {
weakSelf.token = value;
[weakSelf requestHeadDataWithToken:value handle:^(id value) {
NSLog(@"%@",value);
weakSelf.headData = value;
}];
[weakSelf requestListDataWithToken:value handle:^(id value) {
NSLog(@"%@",value);
weakSelf.listData = value;
}];
}];
初级解决方法; sys 堵塞 --- 1: 用户体验 2 : 异步,不知道什么时候请求完成,无法跟踪 //sync 堵塞等待,用户体验差
dispatch_block_t task = ^{
//堵塞等待,体验差
dispatch_sync(queue, ^{
[self requestToken:^(id value) {
weakSelf.token = value;
}];
});
//异步,不知道什么时候调用完成,想异步回调后执行其他任务
dispatch_async(queue, ^{
[weakSelf requestHeadDataWithToken:self.token handle:^(id value) {
NSLog(@"%@",value);
weakSelf.headData = value;
}];
});
dispatch_async(queue, ^{
[weakSelf requestListDataWithToken:self.token handle:^(id value) {
NSLog(@"%@",value);
weakSelf.listData = value;
}];
});
};
dispatch_async(queue, task);
NSLog(@"请求完毕了?我要去其他事情了");
此时 栅栏函数 dispatch_barrier_async 就诞生了
4.2 特点:
- 顺序执行
- 线程安全
for (int i = 0; i<1000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"%zd --- %@ ---- %d",self.mArray.count,[NSThread currentThread],i);
// dispatch_barrier_async(concurrentQueue, ^{//async阻塞队列,sync阻塞队列和线程
[self.mArray addObject:image];
NSLog(@"%zd ===== %@ ====== %d",self.mArray.count,[NSThread currentThread],i);
// });
// @synchronized(self){
// [self.mArray addObject:image];
// }
if (i==199) {
NSLog(@"++++++++:%zd",self.mArray.count);
}
});
}
没有添加栅栏函数的情况下:
数组个数不正常,不是固定的10000个。
添加栅栏函数的情况:
[self.mArray removeAllObjects];
dispatch_queue_t concurrentQueue = dispatch_queue_create("haitao", DISPATCH_QUEUE_CONCURRENT);
// dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);
// signal
for (int i = 0; i<10000; i++) {
dispatch_async(concurrentQueue, ^{
NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"%zd --- %@ ---- %d",self.mArray.count,[NSThread currentThread],i);
dispatch_barrier_async(concurrentQueue, ^{//async阻塞队列,sync阻塞队列和线程
[self.mArray addObject:image];
NSLog(@"%zd ===== %@ ====== %d",self.mArray.count,[NSThread currentThread],i);
});
// @synchronized(self){
// [self.mArray addObject:image];
// }
if (i==199) {
NSLog(@"++++++++:%zd",self.mArray.count);
}
});
}
[self.mArray removeAllObjects];
栅栏函数dispatch_barrier_async保证了每次只调度一个任务的执行,只有一个线程(number = 64, name = (null)} ======)。保证了线程安全,类似于sync函数,但是栅栏函数比sync函数更好的地方是它不会堵塞当前线程
。
4.3 栅栏函数的3个缺点:
4.3.1 不能保证FIFO
栅栏函数dispatch_barrier_async但是不能保证执行的顺序是串行的FIFO,因为任务调度是并发的,任务开始和任务结束的时间不可控:
从下面的打印可以看出来,
先执行任务:
102 111 104 130 131
栅栏函数执行后的任务:
104 131 111 102 130
55 ===== <NSThread: 0x600001e958c0>{number = 5, name = (null)} ====== 104
56 ===== <NSThread: 0x600001e958c0>{number = 5, name = (null)} ====== 131
57 ===== <NSThread: 0x600001e958c0>{number = 5, name = (null)} ====== 111
58 ===== <NSThread: 0x600001e958c0>{number = 5, name = (null)} ====== 102
59 ===== <NSThread: 0x600001e958c0>{number = 5, name = (null)} ====== 130
0 --- <NSThread: 0x600001e92380>{number = 81, name = (null)} ---- 102
0 --- <NSThread: 0x600001eb4900>{number = 46, name = (null)} ---- 111
0 --- <NSThread: 0x600001ea0380>{number = 42, name = (null)} ---- 105
0 --- <NSThread: 0x600001e83740>{number = 82, name = (null)} ---- 103
0 --- <NSThread: 0x600001eb9e00>{number = 43, name = (null)} ---- 104
0 --- <NSThread: 0x600001ebb840>{number = 72, name = (null)} ---- 113
0 --- <NSThread: 0x600001e85b40>{number = 79, name = (null)} ---- 115
0 --- <NSThread: 0x600001e48700>{number = 75, name = (null)} ---- 116
0 --- <NSThread: 0x600001eba800>{number = 77, name = (null)} ---- 112
0 --- <NSThread: 0x600001e92300>{number = 78, name = (null)} ---- 117
0 --- <NSThread: 0x600001efeb80>{number = 73, name = (null)} ---- 114
0 --- <NSThread: 0x600001eba4c0>{number = 74, name = (null)} ---- 118
0 --- <NSThread: 0x600001e48700>{number = 75, name = (null)} ---- 119
0 --- <NSThread: 0x600001e85b40>{number = 79, name = (null)} ---- 120
0 --- <NSThread: 0x600001ebb840>{number = 72, name = (null)} ---- 121
0 --- <NSThread: 0x600001e48700>{number = 75, name = (null)} ---- 122
0 --- <NSThread: 0x600001e85b40>{number = 79, name = (null)} ---- 123
0 --- <NSThread: 0x600001ebb840>{number = 72, name = (null)} ---- 124
0 --- <NSThread: 0x600001e48700>{number = 75, name = (null)} ---- 125
0 --- <NSThread: 0x600001eba4c0>{number = 74, name = (null)} ---- 126
0 --- <NSThread: 0x600001efeb80>{number = 73, name = (null)} ---- 127
0 --- <NSThread: 0x600001ebb840>{number = 72, name = (null)} ---- 128
0 --- <NSThread: 0x600001e92300>{number = 78, name = (null)} ---- 129
0 --- <NSThread: 0x600001eba700>{number = 80, name = (null)} ---- 130
0 --- <NSThread: 0x600001ea0380>{number = 42, name = (null)} ---- 131
0 --- <NSThread: 0x600001e83740>{number = 82, name = (null)} ---- 132
0 --- <NSThread: 0x600001eba800>{number = 77, name = (null)} ---- 133
0 --- <NSThread: 0x600001eb9e00>{number = 43, name = (null)} ---- 134
4.3.2 并不能保证所有任务执行完后执行
栅栏函数dispatch_barrier_async并不能保证所有任务执行完后执行dispatch_barrier_async里面的函数。
4.3.3 afn网络请求不能保证顺序执行
栅栏函数dispatch_barrier_async 是否可以放到网络请求中,保证顺序执行,都在同一个线程中?
答案是NO
。 所有任务都是同一队列才可以。
//加载完毕了 栅栏函数上, afn异步请求的bug,是队列不统一,自定义队列和回调返回的队列不是同一个。
自定义队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
dispatch_barrier_async(concurrentQueue, ^{
//但是afn的队列是
af_url_session_manager_creation_queue = dispatch_queue_create("com.alamofire.networking.session.manager.creation", DISPATCH_QUEUE_SERIAL);
这是两个不同的队列,这样就无法不能保证任务的顺序执行。
5. GCD应用调度组
5.1 背景
#栅栏函数dispatch_barrier_async的第2、3个bug引出了调度组 即使队列不同,但是group是同一个,这样就能保证任务的顺序执行。
//创建调度组
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
// 任务1
})
dispatch_group_async(group, queue, ^{
// 任务2
})
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 任务3
//执行完 任务1 任务2 以后 再执行 任务3
})
如果是网络请求
的话, 个人推荐使用YTKNetwork
, 多个请求同时调用,写法特别简洁。
YKBLoginSendMessageAPI *loginSendAPI = [YKBLoginSendMessageAPI new];
YTKBatchRequest *batchRequest = [[YTKBatchRequest alloc] initWithRequestArray:@[recommendAPI, adlistAPI,hotArticeAPI,loginSendAPI]];
kWeakSelf(self);
[MBProgressHUD showActivityMessageInWindow:nil];
[batchRequest startWithCompletionBlockWithSuccess:^(YTKBatchRequest * _Nonnull batchRequest) {
[MBProgressHUD hideHUD];
[weakself.tableView.mj_header endRefreshing];
[weakself recommendAPISuccess:(YKBCardRecommendAPI *)batchRequest.requestArray[0]];
[weakself adlistAPISuccess:(YKBAdListAPI *)batchRequest.requestArray[1]];
[weakself hotArticeAPiSuuccess:(YKBQuoteArticeListCheckApi *)batchRequest.requestArray[2]];
} failure:^(YTKBatchRequest * _Nonnull batchRequest) {
[weakself.tableView.mj_header endRefreshing];
[MBProgressHUD hideHUD];
[MBProgressHUD showErrorMessage:@"请求失败,请稍后再试"];
}];
5.2 延迟等待
long timeout = dispatch_group_wait(group, 0);
if (timeout == 0) { notify }
5.3 enter - leave
// 问题: 如果 dispatch_group_enter 多 dispatch_group_leave 不会调用通知
// dispatch_group_enter 少 dispatch_group_leave 奔溃
// 成对存在
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"第一个走完了");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"第二个走完了");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"所有任务完成,可以更新UI");
});
6. GCD最大并发数
信号量 dispatch_semaphore_t
控制并发数的意义:可以防止程序的压力,内存消耗等问题。
// 同步
dispatch_semaphore_t lock = dispatch_semaphore_create(1);
begin = CACurrentMediaTime();
for (int i = 0; i < count; i++) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(lock);
}
默认SDWebImage的并发数是6,AFNetWorking的并发数是4。
//AFN最大图片下载任务4个
return [self initWithSessionManager:sessionManager
downloadPrioritization:AFImageDownloadPrioritizationFIFO
maximumActiveDownloads:4
imageCache:[[AFAutoPurgingImageCache alloc] init]];
具体代码: github.com/tanghaitao/…
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//信号量
//总结:由于设定的信号值为3,先执行三个线程,等执行完一个,才会继续执行下一个,保证同一时间执行的线程数不超过3
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
//任务1
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@" --- %@ ---- 任务1",[NSThread currentThread]);
NSLog(@"执行任务1");
sleep(6);//【睡眠6秒】
NSLog(@"任务1完成");
dispatch_semaphore_signal(semaphore);
});
//任务2
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@" --- %@ ---- 任务2",[NSThread currentThread]);
NSLog(@"执行任务2");
sleep(5);// 【睡眠5秒】
NSLog(@"任务2完成");
dispatch_semaphore_signal(semaphore);
});
//任务3
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@" --- %@ ---- 任务3",[NSThread currentThread]);
NSLog(@"执行任务3");
NSLog(@"任务3完成");
dispatch_semaphore_signal(semaphore);
});
--- <NSThread: 0x600003c9f740>{number = 5, name = (null)} ---- 任务2
--- <NSThread: 0x600003cb4180>{number = 4, name = (null)} ---- 任务1
执行任务2
执行任务1
任务2完成
--- <NSThread: 0x600003c90d80>{number = 3, name = (null)} ---- 任务3
执行任务3
任务3完成
任务1完成
分析:一次只执行2个任务,等这2个任务中有1个完成(任务2的睡眠时间短sleep(5)<sleep(6),任务2比任务1先完成)后,此时任务1还在睡眠,马上任务3开始执行,因为任务1还需要睡眠sleep(6-5),1秒钟,所以任务3先完成,接着任务1完成。
上面就是信号量的意义,类似于阀门,每次只允许几个任务执行,如果这几个任务开始执行了,只要有一个任务完成,下面的任务就可以接着执行。
如果dispatch_semaphore_create(1),就类似于串行的效果了。
7. Dispatch_source
应用场景: 倒计时,banner轮播图等等
{
self.queue = dispatch_queue_create("com.haitao", 0);
self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
/**
1: b创建source
2: 绑定回调
3: 触发函数
*/
// runloop ---> source
// 下面的函数是 调用封装source
// 封装任务块 ----> 函数触发
// dispatch_sync(_queue, ^{
//
// });
//
// 保存代码块 ---> 异步 dispatch_source_set_event_handler()
// 封装我们需要回调的触发函数
dispatch_source_set_event_handler(self.source, ^{
NSUInteger value = dispatch_source_get_data(self.source);
self.totalComplete += value;
NSLog(@"进度:%.2f", self.totalComplete/100.0);
self.progressView.progress = self.totalComplete/100.0;
});
self.isRunning = YES;
dispatch_resume(self.source);
// resume (OC): dispatch_resume (c)
// [task resume]
- (IBAction)didClickStartOrPauseAction:(id)sender {
if (self.isRunning) {// 正在跑就暂停
dispatch_suspend(self.source);
dispatch_suspend(self.queue);// mainqueue 挂起
self.isRunning = NO;
[sender setTitle:@"暂停中..." forState:UIControlStateNormal];
}else{
dispatch_resume(self.source);
dispatch_resume(self.queue);
self.isRunning = YES;
[sender setTitle:@"加载中..." forState:UIControlStateNormal];
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"点击开始加载");
for (NSUInteger index = 0; index < 100; index++) {
dispatch_async(self.queue, ^{
if (!self.isRunning) {
NSLog(@"暂停下载");
return ;
}
sleep(2);
dispatch_source_merge_data(self.source, 1);
});
}
}
倒计时:
// GCD倒计时
- (void)startCoundown
{
__block int timeout = showtime + 1; //倒计时时间 + 1
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0 * NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
if(timeout <= 0){ //倒计时结束,关闭
dispatch_source_cancel(_timer);
dispatch_async(dispatch_get_main_queue(), ^{
[self dismiss];
});
}else{
dispatch_async(dispatch_get_main_queue(), ^{
[_countBtn setTitle:[NSString stringWithFormat:@"跳过%d",timeout] forState:UIControlStateNormal];
});
timeout--;
}
});
dispatch_resume(_timer);
}