GCD下的几种实现同步的方式
1.串行队列 2.并行队列 3.分组 4.信号量
今天有一个需求,就是网络请求完,有一个逻辑判断,然后更新界面。而我的网络请求,还有一个返回执行的方法,返回的数据不是在block中。这样我都是先进行网络请求之后的方法,然后才执行网络请求之后的返回方法。这样是错误的。
我的网络请求方法。如下:
解决:把网络请求的返回数据包装在block中,并且网络请求与需要紧跟在网络请求之后执行的方法用GCD实现同步。
实例:网络下载图片并展示在视图上,实现这个需求,可以拆分成两个任务,一个是去网上获取图片,一个是展示在视图上。 这两个任务是有关联的,所以需要同步处理。
下面看这几种方式如何实现:
一、
1.串行队列
1.1GCD相关知识:
(1)GCD下的dispatch_queue队列都是FIFO队列,都会按照提交到队列的顺序执行.
只是根据队列的性质,分为<1>串行队列:用户队列、主线程队列 <2>并行队列.
(2)同步(dispatch_sync)、异步方式(dispatch_async). 配合串行队列和并行队列使用.
1.2
(1).并行队列
采用并行队列的时候,可以采用同步的方式把任务提交到队列里面去,即可以实现同步的方式。
//新建一个队列
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//记时
NSDate *startTime = [NSDate date];
//加入队列
dispatch_async(concurrentQueue, ^{
__block UIImage *image = nil;
//1.先去网上下载图片
dispatch_sync(concurrentQueue, ^{
NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
NSURL *url = [NSURL URLWithString:urlAsString];
NSError *downloadError = nil;
NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
if (downloadError == nil && imageData != nil) {
image = [UIImage imageWithData:imageData];
}
else if(downloadError != nil){
NSLog(@"error happened = %@", downloadError);
}
else{
NSLog(@"No data download");
}
});
//2.在主线程展示到界面里
dispatch_sync(dispatch_get_main_queue(), ^{
if (image != nil) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[imageView setImage:image];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:imageView];
[imageView release];
NSDate *endTime = [NSDate date];
NSLog(@"并行同步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
}
else{
NSLog(@"image isn't downloaded, nothing to display");
}
});
});
(2).串步队列直接提交两个任务就可以
// 串形队列
dispatch_queue_t serilQueue = dispatch_queue_create("com.quains.myQueue", 0);
//开始时间
NSDate *startTime = [NSDate date];
__block UIImage *image = nil;
//1.先去网上下载图片
dispatch_async(serilQueue, ^{
NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
NSURL *url = [NSURL URLWithString:urlAsString];
NSError *downloadError = nil;
NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
if (downloadError == nil && imageData != nil) {
image = [[UIImage imageWithData:imageData] retain];
}
else if(downloadError != nil){
NSLog(@"error happened = %@", downloadError);
}
else{
NSLog(@"No data download");
}
});
//2.在主线程展示到界面里
dispatch_async(serilQueue, ^{
NSLog(@"%@",[NSThread currentThread]);
// 在主线程展示
dispatch_async(dispatch_get_main_queue(), ^{
if (image != nil) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[imageView setImage:image];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:imageView];
[imageView release];
NSDate *endTime = [NSDate date];
NSLog(@"串行异步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
}
else{
NSLog(@"image isn't downloaded, nothing to display");
}
});
});
//3.清理
dispatch_release(serilQueue);
[image release];
注意:
(1) __block变量分配在栈,retain下,防止被回收.
(2)dispatch要手动create和release.
(3)提交到主线程队列的时候,慎用同步dispatch_sync方法,有可能造成死锁. 因为主线程队列是串行队列,要等队列里的任务一个一个执行.所以提交一个任务到队列,如果用同步方法就会阻塞住主线程,而主线程又要等主线程队列里的任务都执行完才能执行那个刚提交的,所以主线程队列里还有其他的任务的话,但他已经被阻塞住了,没法先完成队列里的其他任务,即,最后一个任务也没机会执行到,于是造成死锁.
(4)提交到串行队列可以用同步方式,也可以用异步方式.
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
NSDate *startTime = [NSDate date];
__block UIImage *image = nil;
dispatch_group_async(group, queue, ^{
//1.先去网上下载图片
NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
NSURL *url = [NSURL URLWithString:urlAsString];
NSError *downloadError = nil;
NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
if (downloadError == nil && imageData != nil) {
image = [[UIImage imageWithData:imageData] retain];
}
else if(downloadError != nil){
NSLog(@"error happened = %@", downloadError);
}
else{
NSLog(@"No data download");
}
});
// 2.等下载好了再在刷新主线程
dispatch_group_notify(group, queue, ^{
//在主线程展示到界面里
dispatch_async(dispatch_get_main_queue(), ^{
if (image != nil) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[imageView setImage:image];
[image release];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:imageView];
[imageView release];
NSDate *endTime = [NSDate date];
NSLog(@"分组同步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
}
else{
NSLog(@"image isn't downloaded, nothing to display");
}
});
});
// 释放掉
dispatch_release(group);
dispatch_group 也要手动创建和释放.
dispatch_notify()提供了一个知道group什么时候结束的点. 当然也可以使用dispatch_wait()去阻塞。
// 信号量初始化为1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
NSDate *startTime = [NSDate date];
__block UIImage *image = nil;
//1.先去网上下载图片
dispatch_async(queue, ^{
// wait操作-1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 开始下载
NSString *urlAsString = @"http://avatar.csdn.net/B/2/2/1_u010013695.jpg";
NSURL *url = [NSURL URLWithString:urlAsString];
NSError *downloadError = nil;
NSData *imageData = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:url] returningResponse:nil error:&downloadError];
if (downloadError == nil && imageData != nil) {
image = [[UIImage imageWithData:imageData] retain];
//NSLog(@"heap %@", image);
//NSLog(@"%d",[image retainCount]);
}
else if(downloadError != nil){
NSLog(@"error happened = %@", downloadError);
}
else{
NSLog(@"No data download");
}
// signal操作+1
dispatch_semaphore_signal(semaphore);
});
// 2.等下载好了再在刷新主线程
dispatch_async(dispatch_get_main_queue(), ^{
// wait操作-1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (image != nil) {
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[imageView setImage:image];
NSLog(@"%d",[image retainCount]);
[image release];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:imageView];
[imageView release];
NSDate *endTime = [NSDate date];
NSLog(@"信号量同步 completed in %f time", [endTime timeIntervalSinceDate:startTime]);
}
else{
NSLog(@"image isn't downloaded, nothing to display");
}
// signal操作+1
dispatch_semaphore_signal(semaphore);
});
dispatch_wait会阻塞线程并且检测信号量的值,直到信号量值大于0才会开始往下执行,同时对信号量执行-1操作。
dispatch_signal则是+1操作。
二、
以上几种方式,都是通过阻塞线程的方式去实现同步。
网友评论