博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
GCD下的几种实现同步的方式
阅读量:5140 次
发布时间:2019-06-13

本文共 8561 字,大约阅读时间需要 28 分钟。

GCD多线程下,实现线程同步的方式有如下几种:

1.串行队列 2.并行队列 3.分组 4.信号量

实例: 去网上获取一张图片并展示在视图上. 实现这个需求,可以拆分成两个任务,一个是去网上获取图片,一个是展示在视图上. 这两个任务是有关联的,所以需要同步处理.

下面看这几种方式如何实现.

 

一、

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操作.

 

二、

以上几种方式,都是通过阻塞线程的方式去实现同步。

 

 

 

 

 

转载于:https://www.cnblogs.com/Quains/p/3182823.html

你可能感兴趣的文章
SQL 取时间差 去掉周末及非工作时间节假日
查看>>
MyEclipse+Struts+Hibernate+Mysql开发环境配置
查看>>
创建vue-cil后出现localhost:8080无法访问
查看>>
[HDU 2102] A计划(搜索题,典型dfs or bfs)
查看>>
Eclipse启动Tomcat端口占用
查看>>
建立索引
查看>>
php 中的魔术常量
查看>>
解释下浮动和它的工作原理?清除浮动的技巧
查看>>
改变命运的三个层次
查看>>
Command Line 3
查看>>
设计模式
查看>>
[ASP.NET]Treeview 控件显示服务端目录文件夹及文件
查看>>
dotnet core webapi +vue 搭建前后端完全分离web架构(一)
查看>>
maven打包时生成源代码
查看>>
GetLastError() 返回值含义
查看>>
浏览器的同源策略
查看>>
[转] iptables
查看>>
在Mac系统下如何恢复SourceTree全局忽略的文件
查看>>
设计模式之类关系
查看>>
10年后编程还有意义吗?(转自深氪)
查看>>