GCD多线程下,实现线程同步的方式有以下几种:
1.串行队列 2.并行队列 3.分组 4.信号量
实例: 去网上获取一张图片并展现在视图上. 实现这个需求,能够拆分红两个任务,一个是去网上获取图片,一个是展现在视图上. 这两个任务是有关联的,因此须要同步处理.
下面看这几种方式如何实现.
1、
1.串行队列
1.1[GCD相关:]
(1)GCD下的dispatch_queue队列都是FIFO队列,都会按照提交到队列的顺序执行.
只是根据队列的性质,分为<1>串行队列:用户队列、主线程队列 <2>并行队列.
(2)同步(dispatch_sync)、异步方式(dispatch_async). 配合串行队列和并行队列使用.
1.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)提交到串行队列能够用同步方式,也能够用异步方式.
2.并行队列
采用并行队列的时候,能够采用同步的方式把任务提交到队列里去,便可以实现同步的方式
//新建一个队列
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");
}
});
});
两个同步的任务用一个异步的包起来,提交到并行队列里去,便可实现同步的方式.
3.使用分组方式
3.1 group自己是将几个有关联的任务组合起来,而后提供给开发者一个知道这个group结束的点.
虽然这个只有一个任务,可是能够利用group的结束点,去阻塞线程,从而来实现同步方式.
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()去阻塞.
4.信号量
信号量 和 琐 的做用差很少,能够用来实现同步的方式.
可是信号量一般用在 容许几个线程同时访问一个资源,经过信号量来控制访问的线程个数.
// 信号量初始化为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操做.