一、一个异步、同步、并行、串行的表格
(第一个使用Markdown)
多核是gcd的基础
二、任务:同步、异步、栅栏
同步:dispatch_sync
异步:dispatch_async
栅栏:dispatch_barrier_async
同步和异步我就不举🌰了
栅栏(zha lan)函数(我也不懂,查的百度)
和名字相同,栅栏的作用,等barrier之前的函数执行结束再执行barrier函数,之后的函数必须在barrier函数执行完毕之后才能运行,上代码吧
有栅栏函数
dispatch_queue_t queue = dispatch_queue_create("barrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"第一个异步任务%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"第二个异步任务%@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"栅栏函数%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"第三个异步任务%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"第四个异步任务%@",[NSThread currentThread]);
});
运行结果
2017-05-05 13:22:46.847 retainCount[16150:1140225] 第二个异步任务<NSThread: 0x6000000688c0>{number = 5, name = (null)}
2017-05-05 13:22:46.847 retainCount[16150:1140226] 第一个异步任务<NSThread: 0x6080000707c0>{number = 6, name = (null)}
2017-05-05 13:22:46.848 retainCount[16150:1140226] 栅栏函数<NSThread: 0x6080000707c0>{number = 6, name = (null)}
2017-05-05 13:22:46.849 retainCount[16150:1140226] 第三个异步任务<NSThread: 0x6080000707c0>{number = 6, name = (null)}
2017-05-05 13:22:46.849 retainCount[16150:1140225] 第四个异步任务<NSThread: 0x6000000688c0>{number = 5, name = (null)}
一二任务被栅栏函数分割在上方,三四任务被分割在下方
有个规律,栅栏任务会在上一个任务的同一个线程开始,栅栏任务的下一个任务也会和栅栏任务在同一个线程开始(本人不知道原因)
注释掉栅栏函数之后运行结果
2017-05-05 13:24:26.448 retainCount[16183:1142163] 第四个异步任务<NSThread: 0x600000260980>{number = 6, name = (null)}
2017-05-05 13:24:26.448 retainCount[16183:1142160] 第三个异步任务<NSThread: 0x600000260740>{number = 5, name = (null)}
2017-05-05 13:24:26.448 retainCount[16183:1142180] 第一个异步任务<NSThread: 0x600000260940>{number = 3, name = (null)}
2017-05-05 13:24:26.448 retainCount[16183:1142161] 第二个异步任务<NSThread: 0x608000265ec0>{number = 4, name = (null)}
异步任务时间同时开始,在四个新开的线程中
【栅栏函数整理自http://www.cnblogs.com/denz/p/5277666.html】
三、串行队列和并行队列
队列不想说太多了,之前有一篇已经说过一些了,再说可能会乱。但是看到一个总结感觉很好,写在这里
- 同步异步,影响是否开启新的线程
- 串行并行,影响任务的是否同时执行还是按顺序执行
四、GCD的使用
之前有写,但是感觉这个大牛写的更好,把代码又都写了一遍。
- (void)createUI
{
UIButton *myBtn = [[UIButton alloc]initWithFrame:CGRectMake(20, 20, 300,50)];
myBtn.backgroundColor = [UIColor redColor];
[myBtn setTitle:@"子线程、同步任务、主队列" forState:UIControlStateNormal];
[myBtn addTarget:self action:@selector(ChildSyncMain) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myBtn];
UIButton *myBtn1 = [[UIButton alloc]initWithFrame:CGRectMake(20, 90, 300,50)];
myBtn1.backgroundColor = [UIColor redColor];
[myBtn1 setTitle:@"主线程、同步任务、主队列" forState:UIControlStateNormal];
[myBtn1 addTarget:self action:@selector(MainSyncMain) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myBtn1];
UIButton *myBtn2 = [[UIButton alloc]initWithFrame:CGRectMake(20, 160, 300,50)];
myBtn2.backgroundColor = [UIColor redColor];
[myBtn2 setTitle:@"主线程、异步任务、主队列" forState:UIControlStateNormal];
[myBtn2 addTarget:self action:@selector(MainAsyncMain) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myBtn2];
UIButton *myBtn3 = [[UIButton alloc]initWithFrame:CGRectMake(20, 230, 300,50)];
myBtn3.backgroundColor = [UIColor redColor];
[myBtn3 setTitle:@"同步任务、并发队列" forState:UIControlStateNormal];
[myBtn3 addTarget:self action:@selector(SyncConcurrent) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myBtn3];
UIButton *myBtn4 = [[UIButton alloc]initWithFrame:CGRectMake(20, 300, 300,50)];
myBtn4.backgroundColor = [UIColor redColor];
[myBtn4 setTitle:@"同步任务、串行队列" forState:UIControlStateNormal];
[myBtn4 addTarget:self action:@selector(SyncSerial) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myBtn4];
UIButton *myBtn5 = [[UIButton alloc]initWithFrame:CGRectMake(20, 370, 300,50)];
myBtn5.backgroundColor = [UIColor redColor];
[myBtn5 setTitle:@"异步任务、串行队列" forState:UIControlStateNormal];
[myBtn5 addTarget:self action:@selector(AsyncSerial) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myBtn5];
UIButton *myBtn6 = [[UIButton alloc]initWithFrame:CGRectMake(20, 440, 300,50)];
myBtn6.backgroundColor = [UIColor redColor];
[myBtn6 setTitle:@"异步任务、并行队列" forState:UIControlStateNormal];
[myBtn6 addTarget:self action:@selector(AsyncConcurrent) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myBtn6];
}
- (void)ChildSyncMain
{
//第一个参数是优先级,第二个参数是保留字
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//开启一个子线程
dispatch_async(queue, ^{
//获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
//同步任务
dispatch_sync(queue, ^{
NSLog(@"任务所在线程%@",[NSThread currentThread]);
});
});
}
- (void)MainSyncMain
{
//主队列,此时在主线程
dispatch_queue_t queue = dispatch_get_main_queue();
//同步任务
dispatch_sync(queue, ^{
NSLog(@"任务所在线程%@",[NSThread currentThread]);
});
}
- (void)MainAsyncMain
{
//与上一函数区别在于异步
dispatch_queue_t queue = dispatch_get_main_queue();
//异步任务
dispatch_async(queue, ^{
NSLog(@"任务所在线程%@",[NSThread currentThread]);
});
}
- (void)SyncConcurrent
{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//三个同步任务,放在并发队列,查看所在线程
dispatch_sync(queue, ^{
NSLog(@"1------所在线程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2------所在线程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3------所在线程%@",[NSThread currentThread]);
});
}
- (void)SyncSerial
{
dispatch_queue_t queue = dispatch_queue_create("firstSerial", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"1------所在线程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2------所在线程%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3------所在线程%@",[NSThread currentThread]);
});
}
- (void)AsyncSerial
{
dispatch_queue_t queue = dispatch_queue_create("SecondSerial", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"1------所在线程%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2------所在线程%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3------所在线程%@",[NSThread currentThread]);
});
}
//这个大牛写的太好了,不好意思抄了,直接copy过来吧。参数什么的解释的太好了
- (void)AsyncConcurrent
{
/*
执行任务
dispatch_async
dispatch_sync
*/
/*
第一个参数: 队列的名称
第二个参数: 告诉系统需要创建一个并发队列还是串行队列
DISPATCH_QUEUE_SERIAL :串行
DISPATCH_QUEUE_CONCURRENT 并发
*/
// dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_CONCURRENT);
// 系统内部已经给我们提供好了一个现成的并发队列
/*
第一个参数: iOS8以前是优先级, iOS8以后是服务质量
iOS8以前
* - DISPATCH_QUEUE_PRIORITY_HIGH 高优先级 2
* - DISPATCH_QUEUE_PRIORITY_DEFAULT: 默认的优先级 0
* - DISPATCH_QUEUE_PRIORITY_LOW: 低优先级 -2
* - DISPATCH_QUEUE_PRIORITY_BACKGROUND:
iOS8以后
* - QOS_CLASS_USER_INTERACTIVE 0x21 用户交互(用户迫切想执行任务)
* - QOS_CLASS_USER_INITIATED 0x19 用户需要
* - QOS_CLASS_DEFAULT 0x15 默认
* - QOS_CLASS_UTILITY 0x11 工具(低优先级, 苹果推荐将耗时操作放到这种类型的队列中)
* - QOS_CLASS_BACKGROUND 0x09 后台
* - QOS_CLASS_UNSPECIFIED 0x00 没有设置
第二个参数: 废物
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
/*
第一个参数: 用于存放任务的队列
第二个参数: 任务(block)
GCD从队列中取出任务, 遵循FIFO原则 , 先进先出
输出的结果和苹果所说的原则不符合的原因: CPU可能会先调度其它的线程
能够创建新线程的原因:
我们是使用"异步"函数调用
能够创建多个子线程的原因:
我们的队列是并发队列
*/
dispatch_async(queue, ^{
NSLog(@"任务1 == %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2 == %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务3 == %@", [NSThread currentThread]);
});
}
这是个带按钮的操作,效果主要看打印,恩界面是这样的
gcd操作界面.png打印是这样的(第二个按钮会造成死锁因此就没有打印)
2017-05-05 15:34:33.983 retainCount[17357:1229839] 任务所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:39.538 retainCount[17357:1229839] 任务所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:41.420 retainCount[17357:1229839] 1------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:41.421 retainCount[17357:1229839] 2------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:41.422 retainCount[17357:1229839] 3------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:42.729 retainCount[17357:1229839] 1------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:42.730 retainCount[17357:1229839] 2------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:42.731 retainCount[17357:1229839] 3------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:43.561 retainCount[17357:1232087] 1------所在线程<NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:43.561 retainCount[17357:1232087] 2------所在线程<NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:43.562 retainCount[17357:1232087] 3------所在线程<NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:44.402 retainCount[17357:1232087] 任务1 == <NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:44.402 retainCount[17357:1232083] 任务3 == <NSThread: 0x600000276e80>{number = 5, name = (null)}
2017-05-05 15:34:44.402 retainCount[17357:1232112] 任务2 == <NSThread: 0x60800026bf40>{number = 4, name = (null)}
具体的分析统一写在这里吧
首先还是得记住前面所写的
- 同步异步决定是否开启线程
- 并行串行决定是否顺序执行任务
- 主队列中有主线程,在其中的任务不会创建新的线程
记住这三条就可以了
好了,开始分析
-
子线程、同步任务、主队列
看这个解释之前先看一下第二中情况,这一次我们在子线程中开启了同步任务,然后block放在主队列,我们的同步任务只要等到block执行就可以了,不会死锁。 -
主线程、同步任务、主队列
关于主线程和主队列的概念,我看了好几个博客,还是不太懂,不过有一个原则就是主队列中的任务要在主线程中执行,有了这个原则可能就好解释了。我们在主线程中调用了同步函数,调用的同步函数的操作就在block中。但是block被我们放在了主队列中,主队列是串行队列,block在这个队列的最后。前面的所有任务如果无法完成就不会调用到block。但是前面任务完成的条件是同步函数要完成。于是就死锁了。 -
主线程、异步任务、主队列
唯一和以上不同的是,异步任务的优先级低,不会抢夺系统资源,会在cpu空闲的时候调用,因此不会造成死锁。 -
同步任务、并发队列
同步任务不会创建新的线程,因此会在主线程中调用 -
同步任务、串行队列
同上 -
异步任务、串行队列
异步任务会开启新的线程,因为串行队列,只开启一个新的线程 -
异步任务、并行队列
异步任务会开启新的线程,因为并行队列,会开启多个线程
五、线程通信
举个图片下载的🌰
- (void)threadCommunication
{
//创建一个队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//异步任务
dispatch_async(queue, ^{
//下载图片
NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1493985090619&di=43c16e2e96b2bd4f2cb694ffd7ad0c0c&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fforum%2Fw%3D580%2Fsign%3Daa1a5e38afec8a13141a57e8c7029157%2F0092bc389b504fc22854cad2e3dde71191ef6d1e.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
//主线程更新ui
dispatch_sync(dispatch_get_main_queue(), ^{
self.ThreadImageView.image = image;
});
NSLog(@"图片下载结束");
});
}
运行结果
线程通信.png五、队列组
使用场景:等俩那个异步操作执行结束后执行某项操作,栅栏函数也可以实现
关键函数
dispatch_group_t//创建队列组
dispatch_group_async//队列其中一个异步任务
dispatch_ground_notify//所有异步操作结束后的操作
示例代码(附带图片绘制和缓存沙盒)
dispatch_queue_t queue = dispatch_queue_create("yfq", DISPATCH_QUEUE_CONCURRENT);
__block UIImage *image1 = nil;
__block UIImage *image2 = nil;
//block内部可以使用block外部的变量,如果需要改变block外部变量,需要添加__block修饰符
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1494220438978&di=5fb8127873860c26a59e9ef775030528&imgtype=0&src=http%3A%2F%2Fd.5857.com%2Fjryh_170321%2Fdesk_001.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
image1 = [UIImage imageWithData:data];
NSLog(@"图片1下载完成");
});
dispatch_group_async(group, queue, ^{
NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1494220164621&di=8bd30fe6a3bde6463dc5dd5c0684009a&imgtype=0&src=http%3A%2F%2Fimage.tianjimedia.com%2FuploadImages%2F2015%2F164%2F22%2FX3UBUK29Z641.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
image2 = [UIImage imageWithData:data];
NSLog(@"图片2下载完成");
});
dispatch_group_notify(group, queue, ^{
UIGraphicsBeginImageContext(CGSizeMake(200, 100));
[image1 drawInRect:CGRectMake(0, 0, 100, 100)];
[image2 drawInRect:CGRectMake(100, 0, 100, 100)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
self.GrounpImageView.image = newImage;
NSString *path = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"123.png"];
[UIImagePNGRepresentation(newImage) writeToFile:path atomically:YES];
});
});
沙盒中的截图
sandboxImage.png通过NSHomeDirectory()打印直接进入模拟器的沙盒,可以看到图片保存了。
图片绘制照搬大体分为几个步骤吧
- UIGraphicsBeginImageContext(cg)//绘制区域
- [image drawInRect:cg];//添加内容
- UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();//获取绘制内容的图片
- UIGraphicsEndImageContext();//关闭图形上下文
绘制图片这块好像有点复杂,一句两句说不清,有空再开一篇说吧。
Markdown自带的表格代码不好用(可能是我没用对),使用没效果,不过自动保存很好用,再也不用担心不小心关闭浏览器了。
【转载并整理于http://www.jianshu.com/p/bc45569adee2】
网友评论