iOS多线程--GCD

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

简单介绍下GCD:

GCD:Grand Central Dispatch(伟大的中枢调度器),纯C的函数,提供了很多非常强大的功能。

它是苹果公司为多核的并行运算提出的解决方案,具有以下优点

1.GCD会自动利用更多的CPU内核(双核、四核等)

2.自动管理线程的生命周期(创建任务、调度任务、销毁线程)

3.程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码,所以它大大解放了程序员

在GCD中有两个核心的概念:任务和队列。任务就是你要执行什么操作 队列就是用来存放任务

使用GCD一般有两个步骤:

1.定制任务,确定你要进行的操作

2.将任务添加到队列中,GCD会自动将任务从队列中取出(FIFO,先进先出原则),放到相应的线程中执行

GCD中有两个主要执行任务的函数:

1.同步:(不能开启新线程)

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block)

2.异步:(开启新线程)

dispatch_async(dispatch_queue_t queue, dispatch_block_t block)

GCD中的队列有可以分为两大类型:

1.并发队列(Concurrent Dispatch Queue):可以让多个任务并发(同时)执行(自动开启多个线程执行任务),并发功能只有在异步函数下才有效(dispatch_async)

2.串行队列:任务一个接一个有序执行

GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建:使用dispatch_get_global_queue函数获得全局的并发队列

串行队列有两种方式获得:1.dispatch_queue_create创建 2.使用主队列dispatch_get_main_queue(),主队列是GCD自带的一种特殊的串行队列,放在主队列的任务都会在主线程中执行

上代码验证下:

1.异步函数在并发队列中执行

- (void)viewDidLoad {

[super viewDidLoad];//1.获得全局的并发队列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);//2.添加任务到队列中执行任务dispatch_async(queue, ^{

NSLog(@"下载1----%@",[NSThread currentThread]);

});

dispatch_async(queue,^{

NSLog(@"下载2----%@",[NSThread currentThread]);

});

dispatch_async(queue,^{

NSLog(@"下载3----%@",[NSThread currentThread]);

});

};

结果如图:

可见程序在多线程中并发执行

2.异步函数在串行队列中执行:

- (void)viewDidLoad {

[super viewDidLoad];

[self asyncSerialQueue];

}#pragmamark 异步+串行

-(void)asyncSerialQueue

{//1.创建串行队列dispatch_queue_t queue = dispatch_queue_create("gcd.queue", NULL);//2.添加任务到队列中执行任务dispatch_async(queue, ^{

NSLog(@"下载1----%@",[NSThread currentThread]);

});

dispatch_async(queue,^{

NSLog(@"下载2----%@",[NSThread currentThread]);

});

dispatch_async(queue,^{

NSLog(@"下载3----%@",[NSThread currentThread]);

});

}

结果如图:

可见程序开启单个线程按顺序执行

3.在同步函数中执行并发任务

- (void)viewDidLoad {

[super viewDidLoad];

[self syncConcurrentQueue];

}#pragmamark 同步+并发

-(void)syncConcurrentQueue

{//1.获得全局的并发队列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);//2.添加任务到队列中执行任务dispatch_sync(queue, ^{

NSLog(@"下载1----%@",[NSThread currentThread]);

});

dispatch_sync(queue,^{

NSLog(@"下载2----%@",[NSThread currentThread]);

});

dispatch_sync(queue,^{

NSLog(@"下载3----%@",[NSThread currentThread]);

});

}

结果如图:

在主线程中按顺序执行,并发队列失去并发功能

4.同步执行串行队列:

- (void)viewDidLoad {

[super viewDidLoad];

[self syncSerialQueue];

}#pragmamark 同步+串行

-(void)syncSerialQueue

{//1.创建串行队列dispatch_queue_t queue = dispatch_queue_create("gcd.queue", NULL);//2.添加任务到队列中执行任务dispatch_sync(queue, ^{

NSLog(@"下载1----%@",[NSThread currentThread]);

});

dispatch_sync(queue,^{

NSLog(@"下载2----%@",[NSThread currentThread]);

});

dispatch_sync(queue,^{

NSLog(@"下载3----%@",[NSThread currentThread]);

});

}

结果同第三种情况,不会开启新的线程,按顺序执行

这里要特别注意,如果使用同步函数在主队列中执行,会造成线程死锁的情况

- (void)viewDidLoad

{

[super viewDidLoad];

NSLog(@"1");

dispatch_sync(dispatch_get_main_queue(),^{

NSLog(@"2");

});

NSLog(@"3");

}

输出结果为1

那么使用异步函数在主队列中执行呢?

- (void)viewDidLoad

{

[super viewDidLoad];

NSLog(@"1----%@",[NSThread currentThread]);

dispatch_async(dispatch_get_main_queue(),^{

NSLog(@"2----%@",[NSThread currentThread]);

});

NSLog(@"3----%@",[NSThread currentThread]);

}

结果为:

结果很特殊:不会开启新的线程,而是主队列中的任务会缓一缓,最后执行

综上来看,各种队列执行情况如下表:

关于GCD还有其他用法

1.延时执行:

- (void)viewDidLoad

{

[super viewDidLoad];//通常的延时执行dispatch_async(dispatch_get_main_queue(), ^{

[self performSelector:@selector(run) withObject:nil afterDelay:1.0];

});//gcd函数执行dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_time_t when= dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0*NSEC_PER_SEC));

dispatch_after(when, queue^{

[self run];

});

}

2.保证某段代码在执行过程中只执行一次(可用于实现单例模式):

- (void)viewDidLoad

{

[super viewDidLoad];staticdispatch_once_t onceToken;

dispatch_once(&onceToken, ^{//执行一次的操作代码});

}

3.队列组(通常用于耗时操作)

例如下载两张图片,要等两张图片都下载完成后,合并两张图,再更新主界面。由于下载图片时间长短不可控,使用普通方法会很困难,这时使用队列组可高效解决

- (void)viewDidLoad

{

[super viewDidLoad];//创建一个组dispatch_group_t group =dispatch_group_create();//下载图片1__block UIImage *image1 =nil;

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

image1= [self downloadImageWithUrl:@"https://www.baidu.com/img/bdlogo.png"];

});//下载图片2__block UIImage *image2 =nil;

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

image2= [self downloadImageWithUrl:@"http://img1.bdstatic.com/static/home/widget/search_box_home/logo/home_white_logo_0ddf152.png"];

});//group任务都执行完毕,再执行其他操作dispatch_group_notify(group, dispatch_get_main_queue(), ^{//合并图片UIGraphicsBeginImageContext(CGSizeMake(300,300));

[image1 drawInRect:CGRectMake(0,0,150,300)];

[image2 drawInRect:CGRectMake(150,0,150,300)];

UIGraphicsEndImageContext();

});

}-(UIImage *)downloadImageWithUrl:(NSString *)urlStr

{

NSURL*url =[NSURL URLWithString:urlStr];

NSData*data =[NSData dataWithContentsOfURL:url];

UIImage*image =[UIImage imageWithData:data];returnimage;

}

4.定时器

- (void)viewDidLoad

{

[super viewDidLoad];

dispatch_source_t timer= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0,0, dispatch_get_main_queue());

dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 15ull*NSEC_PER_SEC, 1ull*NSEC_PER_SEC);

dispatch_source_set_event_handler(timer,^{

NSLog(@"wake up");

dispatch_source_cancel(timer);

});

dispatch_source_set_cancel_handler(timer,^{

NSLog(@"cancel");

});//启动dispatch_resume(timer);

}

此外,gcd还可监听文件的读和写,信号的中断等,有兴趣的可以进一步钻研

示例代码可从Github下载https://github.com/VampireTMAC/GCDdemo

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航