GCD下怎么样实现同步操作?这里有四种思路。
第一串行队列,第二并行队列,第三分组,第四信号量。
GCD下的dispatch_queue队列都是FIFO队列,就是按照提交到队列中的顺序执行,那这里分串行队列和并行队列是什么情况呢?串行队列是提交到队列里的任务按照提交的顺序执行,并且是执行完了上一个任务后再执行下一个任务;并行队列同样按照提交的顺序执行,不过不想串行队列一个任务执行完了后再执行下一个任务,而是可以以任何顺序执行。串行队列只开一个线程,并行队列开启多个线程同时执行任务。
--------------------------------------
常见的实例:去网上获取一张图片,然后展示在视图上。分成两步先去下载图片,然后显示在视图上,这两步是同步的。
一.串行队列
把这两个任务先后提交到一个串行队列里。
// 创建一个串形队列
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 = @"xxx";
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];
NSDate *endTime = [NSDate date];
}
else{
NSLog(@"image is empty");
}
});
});
dispatch_release(serilQueue);
提交到主线程队列的时候,慎用同步dispatch_sync方法,有可能造成死锁。因为主线程队列是串行队列,要等队列里的任务一个一个执行。所以提交一个任务到队列,如果用同步方法就会阻塞住主线程,而主线程又要等主线程队列里的任务都执行完才能执行那个刚提交的,所以主线程队列里还有其他的任务的话,但他已经被阻塞住了,没法先完成队列里的其他任务,即最后一个任务也没机会执行到,于是造成死锁。提交到串行队列可以用同步方式,也可以用异步方式。二.并行队列
采用同步的方式把任务提交到队列里,这样就可以实现同步。
// 创建一个并行队列
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 = @"xxx";
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];
NSDate *endTime = [NSDate date];
}
else {
NSLog(@"image is empty");
}
});
});
两个任务异步提交一个并行队列里面,在这个并行队列里面,同步的执行这两个任务,达到需要。三.分组
group将有关联的几个任务组合起来,然后提供给开发者一个知道这个group结束的点。
下载图片这只是一个任务,虽然这只有一个任务,但是可以用group的结束点,去阻塞线程,以便达到同步的效果。
dispatch_group_t group = dispatch_group_create(); // 创建一个group
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 = @"xxx";
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_group_notify(group, queue, ^{
// 在主线程展示到界面里
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];
NSDate *endTime = [NSDate date];
}
else {
NSLog(@"image is empty");
}
});
});
// 释放掉
dispatch_release(group);
dispatch_notify()提供了一个知道group什么时候结束的点,当然也可以使用dispatch_wait()去阻塞。四.信号量
信号量和锁的作用差不多,可以用来实现同步方式。
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 = @"xxx";
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");
}
// 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];
[imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.view addSubview:imageView];
NSDate *endTime = [NSDate date];
}
else {
NSLog(@"image is empty");
}
// signal操作+1
dispatch_semaphore_signal(semaphore);
});
dispatch_wait会阻塞线程并且检测信号量的值,直到信号量值大于0才会开始往下执行,同时对信号量执行-1操作。
dispatch_signal则是+1操作。
借鉴:http://www.cnblogs.com/Quains/archive/2013/07/10/3182823.html