练习题数组
- (void)viewDidLoad { [super viewDidLoad]; __block int a = 0; while (a < 5) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ a++; }); } NSLog(@"a = %d", a); } 复制代码
A、 a = 0 B、a < 5 C、a = 5 D、a > 5安全
答案:C、Dbash
答案分析markdown
打印结果:并发
看到打印结果很明显浪费了不少性能,想直接打到正确的结果仍是有必定的风险,要解决这风险咱们能够尝试一下使用信号量异步
//当value 等于 1 时,会起到锁的效果,一次一个执行。
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
// timeout = DISPATCH_TIME_FOREVER ,意味没有回来,就一直等待,加锁的效果
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
//解锁
dispatch_semaphore_signal(sem);
复制代码
优化代码async
- (void)viewDidLoad { [super viewDidLoad]; //dispatch_semaphore_create(long value);当value 等于 1 时,会起到锁的效果,一次一个执行。 dispatch_semaphore_t sem = dispatch_semaphore_create(1); __block int a = 0; while (a < 5) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"里面 a = %d----%@", a, [NSThread currentThread]); a++; dispatch_semaphore_signal(sem); }); // timeout = DISPATCH_TIME_FOREVER ,意味没有回来,就一直等待 dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);// } NSLog(@"a = %d", a); } 复制代码
打印结果:函数
答案解析性能
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
复制代码
主要起到控制任务执行顺序,起到同步效果优化
dispatch_barrier_async
前面的任务执行完毕才会到这里
dispatch_barrier_sync
做用相同,可是这个会堵塞线程,影响后面的任务执行
注意,栅栏函数只能控制同一并发队列
- (void)demo2{ dispatch_queue_t concurrentQueue = dispatch_queue_create("janice", DISPATCH_QUEUE_CONCURRENT); /* 1. 异步函数 */ dispatch_async(concurrentQueue, ^{ sleep(1); NSLog(@"123"); }); /* 2. 栅栏函数 */ dispatch_barrier_async(concurrentQueue, ^{ NSLog(@"---------------------%@------------------------",[NSThread currentThread]); }); /* 3. 异步函数 */ dispatch_async(concurrentQueue, ^{ NSLog(@"加载那么多,喘口气!!!"); }); NSLog(@"************起来干!!"); } 复制代码
打印结果:
- (void)demo3{ dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT); // signal -- 线程BUG for (int i = 0; i<2000; 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]; [self.mArray addObject:image]; }); } } 复制代码
打印结果
有个问题,添加了2000张图片,可是实际打印效果只有 1997,这是为何呢,说明线程不安全。
优化办法,在图片加入到数组的时候,添加一个栅栏
- (void)demo3{ // 顺序执行 // 线程安全 dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT); // signal -- 线程BUG for (int i = 0; i<2000; 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]; dispatch_barrier_async(concurrentQueue, ^{ [self.mArray addObject:image]; }); }); } } 复制代码
打印结果:
就是添加一个栅栏来保障其线程安全
将自定义并发队列换成全局并发队列。
- (void)demo3{ dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0); // signal -- 线程BUG for (int i = 0; i<2000; 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]; dispatch_barrier_async(concurrentQueue, ^{ [self.mArray addObject:image]; }); }); } } 复制代码
崩溃了,使用栅栏函数的时候,必定要注意,必定是自定义的并发队列,全局队列是整个系统都在用,在这了使用的情景会就会形成堵塞,因此就会崩溃
总结:
做用:控制任务执行顺序
- (void)groupDemo{ //建立调度组 dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_queue_t queue1 = dispatch_queue_create("com.lg.cn", DISPATCH_QUEUE_CONCURRENT); // SIGNAL dispatch_group_async(group, queue, ^{ NSString *logoStr = @"http://p.qpic.cn/qqcourse/QFzQYCgCrxlq7n5Jats36WGb4wxzJIYmFplnUUgdxk4gy66xicbmGCDM5DXDuVNQP/"; NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr]]; UIImage *image = [UIImage imageWithData:data]; [self.mArray addObject:image]; }); dispatch_group_async(group, queue1, ^{ sleep(2); NSString *logoStr = @"https://www.baidu.com/img/baidu_resultlogo@2.png"; NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr]]; UIImage *image = [UIImage imageWithData:data]; [self.mArray addObject:image]; }); __block UIImage *newImage = nil; dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"数组个数:%ld",self.mArray.count); for (int i = 0; i<self.mArray.count; i++) { UIImage *waterImage = self.mArray[i]; newImage = [KC_ImageTool kc_WaterImageWithWaterImage:waterImage backImage:newImage waterImageRect:CGRectMake(20, 100*(i+1), 100, 40)]; } self.imageView.image = newImage; }); } 复制代码
dispatch_semaphore_t
复制代码
基本使用
- (void)viewDidLoad { [super viewDidLoad]; dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 信号量 -- gcd控制并发数 // 同步 //总结:因为设定的信号值为3,先执行三个线程,等执行完一个,才会继续执行下一个,保证同一时间执行的线程数不超过3 dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); //任务1 dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"执行任务1"); sleep(1); NSLog(@"任务1完成"); dispatch_semaphore_signal(semaphore); }); //任务2 dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"执行任务2"); sleep(1); NSLog(@"任务2完成"); dispatch_semaphore_signal(semaphore); }); //任务3 dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"执行任务3"); sleep(1); NSLog(@"任务3完成"); dispatch_semaphore_signal(semaphore); }); } 复制代码
打印结果:
在任一线程上调用它的一个函数 dispatch_source_merge_data 后,会执行 Dispatch Source 实现定义好的句柄(能够把句柄简单理解为一个 block)这个过程叫 Custom event ,用户事件,是dispatch source 支持处理的一种事件。
句柄是一种指向指针的指针,它指向的就是一个类或者结构,它和系统有很密切的关系
HINSTANCE(实例句柄),HBITMAP(位图句柄),HDC(设备表述句柄),HICON(图标句柄)等。这当中还有一个通用的句柄,就是 HANDLE,好比下面的语句:
ViewController.m
#import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UIProgressView *progressView; @property (nonatomic, strong) dispatch_source_t source; @property (nonatomic, strong) dispatch_queue_t queue; @property (nonatomic, assign) NSUInteger totalComplete; @property (nonatomic) BOOL isRunning; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.totalComplete = 0; self.queue = dispatch_queue_create("com.tz.cn.janice", 0); 第一个参数:dispatch_source_type_t type为设置GCD源方法的类型,前面已经列举过了。 第二个参数:uintptr_t handle Apple的API介绍说,暂时没有使用,传0便可。 第三个参数:unsigned long mask Apple的API介绍说,使用DISPATCH_TIMER_STRICT,会引发电量消耗加重,毕竟要求精确时间,因此通常传0便可,视业务状况而定。 第四个参数:dispatch_queue_t _Nullable queue 队列,将定时器事件处理的Block提交到哪一个队列之上。能够传Null,默认为全局队列。注意:当提交到全局队列的时候,时间处理的回调内,须要异步获取UI线程,更新UI...不过这好像是常识,又啰嗦了... */ self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue()); // 保存代码块 ---> 异步 dispatch_source_set_event_handler() // 设置取消回调 dispatch_source_set_cancel_handler(dispatch_source_t source,dispatch_block_t _Nullable handler) // 封装咱们须要回调的触发函数 -- 响应 dispatch_source_set_event_handler(self.source, ^{ NSUInteger value = dispatch_source_get_data(self.source); // 取回来值 1 响应式 self.totalComplete += value; NSLog(@"进度:%.2f", self.totalComplete/100.0); self.progressView.progress = self.totalComplete/100.0; }); self.isRunning = YES; dispatch_resume(self.source); - (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); // source 值响应 }); } } 复制代码
打印结果: