练习题数组
- (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
答案分析并发
打印结果:异步
看到打印结果很明显浪费了不少性能,想直接打到正确的结果仍是有必定的风险,要解决这风险咱们能够尝试一下使用信号量async
//当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);
复制代码
优化代码函数
- (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);
复制代码
主要起到控制任务执行顺序,起到同步效果ui
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 值响应
});
}
}
复制代码
打印结果: