总结iOS 多线程学习过程五
2015-11-16 19:55
423 查看
GCD队列组 -- dispatch_group_t
为了方便管理队列里的任务,把队列加到一个组里,当组中任务完成后,队列组会通过dispatch_group_notify通知用户.
- (void)viewDidLoad {
[super viewDidLoad];
//全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//创建group对象
dispatch_group_t group = dispatch_group_create();
//将任务提到group中
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"第一个图片下载完毕%@", [NSThread currentThread]);
});
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"第二个图片下载完毕:%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"第三个图片下载完毕:%@",[NSThread currentThread]);
});
//通知group中的任务执行完了(回到主线程)
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//三个图片已经全部下载完了(子线程)
NSLog(@"三个图片下载完毕:%@", [NSThread currentThread]);
});
}
执行结果 :
2015-11-16 19:21:03.130 pthread[1841:163756] 第三个图片下载完毕:<NSThread: 0x7fe34141f2d0>{number = 2, name = (null)}
2015-11-16 19:21:04.135 pthread[1841:163595] 第二个图片下载完毕:<NSThread: 0x7fe341505770>{number = 1, name = main}
2015-11-16 19:21:07.130 pthread[1841:163757] 第一个图片下载完毕<NSThread: 0x7fe34140b4f0>{number = 3, name = (null)}
2015-11-16 19:21:07.130 pthread[1841:163595] 三个图片下载完毕:<NSThread: 0x7fe341505770>{number = 1, name = main}
----------------------------------------华丽的分割线----------------------------------------
GCD 一次性任务:dispatch_once_t
一次性任务在整个程序生命周期中只运行一次仅一次.
- (void)viewDidLoad {
[super viewDidLoad];
for (int i = 0; i < 3; i++) {
NSLog(@"进入for循环,执行次数:%d",i);
//创建一个静态的一次性任务对象
static dispatch_once_t onceToken;
//执行一次性任务
dispatch_once(&onceToken, ^{
//只运行一次仅一次
NSLog(@"====只运行一次====");
});
}
}执行结果:
2015-11-16 19:30:33.530 Demo04-GCD-Group[1894:169586] 进入for循环,执行次数:0
2015-11-16 19:30:33.530 Demo04-GCD-Group[1894:169586] ====只运行一次====
2015-11-16 19:30:33.530 Demo04-GCD-Group[1894:169586] 进入for循环,执行次数:1
2015-11-16 19:30:33.531 Demo04-GCD-Group[1894:169586] 进入for循环,执行次数:2
可以看出来只有一次...
利用一次性任务实现线程安全的单例.
下面用对比的方法来看安全与不安全的区别:
首先创建一个类DataCenter,对这个类设计单例方法,如下:
.h:
#import <Foundation/Foundation.h>
@interface DataCenter : NSObject
//API(Application Program Interface)
//线程不安全创建单例方法
+ (id)sharedDataCenterByUnsafe;
//线程安全创建单例方法
+ (id)sharedDataCenterBySafe;
//initialize方式创建创建单例对象(线程安全->了解)
+ (id)sharedDataCenterByInitialize;.m:
2015-11-16 19:38:03.930 Demo01-Singleton[1948:174311] 线程不安全的单例对象地址:0x7fa862431e10
2015-11-16 19:38:03.930 Demo01-Singleton[1948:174312] 线程不安全的单例对象地址:0x7fa8625375a0
2015-11-16 19:38:06.931 Demo01-Singleton[1948:174316] 线程安全的单例对象地址:0x7fa86253aff0
2015-11-16 19:38:06.931 Demo01-Singleton[1948:174315] 线程安全的单例对象地址:0x7fa86253aff0
----------------------------------------华丽的分割线----------------------------------------
GCD 延迟执行:
直接上代码了:
- (void)viewDidLoad {
[super viewDidLoad];
[self delayTaskByPerform:nil];
[self delayTaskByGCD:nil];
}
- (IBAction)delayTaskByPerform:(id)sender {
//调用perform延迟方法(5s)
[self performSelector:@selector(excuteTask) withObject:nil afterDelay:5];
//实现延迟的任务逻辑
NSLog(@"执行完毕");
}
- (void)excuteTask {
//主线程
for (int i = 0; i < 10; i++) {
//阻塞主线程
[NSThread sleepForTimeInterval:1];
NSLog(@"++++++%@", [NSThread currentThread]);
}
}
- (IBAction)delayTaskByGCD:(id)sender {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//主线程执行
NSLog(@"延迟任务");
});
NSLog(@"延迟任务执行完毕");
}执行结果:
2015-11-16 19:51:02.422 Demo05-GCD-Once[2011:180834] 执行完毕
2015-11-16 19:51:02.423 Demo05-GCD-Once[2011:180834] 延迟任务执行完毕
2015-11-16 19:51:08.430 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:09.435 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:10.438 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:11.443 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:12.445 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:13.447 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:14.450 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:15.451 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{n
9bd0
umber = 1, name = main}
2015-11-16 19:51:16.452 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:17.454 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:17.454 Demo05-GCD-Once[2011:180834] 延迟任务
总结:
第一种是[self performSelector: withObject: afterDelay:];
第二种是dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//主线程执行
//code
});
---------------------------------华丽的分割线---------------------------------
GCD 基本上的内容就差不多了.
从上面代码中也发现了回到主线程的方法了吧!!!
在子线程中使用dispatch_async(dispatch_get_mainqueue(),^{
//code
});讲了"老大",接下来讲"老二了"...
为了方便管理队列里的任务,把队列加到一个组里,当组中任务完成后,队列组会通过dispatch_group_notify通知用户.
- (void)viewDidLoad {
[super viewDidLoad];
//全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//创建group对象
dispatch_group_t group = dispatch_group_create();
//将任务提到group中
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"第一个图片下载完毕%@", [NSThread currentThread]);
});
dispatch_group_async(group, dispatch_get_main_queue(), ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"第二个图片下载完毕:%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"第三个图片下载完毕:%@",[NSThread currentThread]);
});
//通知group中的任务执行完了(回到主线程)
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//三个图片已经全部下载完了(子线程)
NSLog(@"三个图片下载完毕:%@", [NSThread currentThread]);
});
}
执行结果 :
2015-11-16 19:21:03.130 pthread[1841:163756] 第三个图片下载完毕:<NSThread: 0x7fe34141f2d0>{number = 2, name = (null)}
2015-11-16 19:21:04.135 pthread[1841:163595] 第二个图片下载完毕:<NSThread: 0x7fe341505770>{number = 1, name = main}
2015-11-16 19:21:07.130 pthread[1841:163757] 第一个图片下载完毕<NSThread: 0x7fe34140b4f0>{number = 3, name = (null)}
2015-11-16 19:21:07.130 pthread[1841:163595] 三个图片下载完毕:<NSThread: 0x7fe341505770>{number = 1, name = main}
----------------------------------------华丽的分割线----------------------------------------
GCD 一次性任务:dispatch_once_t
一次性任务在整个程序生命周期中只运行一次仅一次.
- (void)viewDidLoad {
[super viewDidLoad];
for (int i = 0; i < 3; i++) {
NSLog(@"进入for循环,执行次数:%d",i);
//创建一个静态的一次性任务对象
static dispatch_once_t onceToken;
//执行一次性任务
dispatch_once(&onceToken, ^{
//只运行一次仅一次
NSLog(@"====只运行一次====");
});
}
}执行结果:
2015-11-16 19:30:33.530 Demo04-GCD-Group[1894:169586] 进入for循环,执行次数:0
2015-11-16 19:30:33.530 Demo04-GCD-Group[1894:169586] ====只运行一次====
2015-11-16 19:30:33.530 Demo04-GCD-Group[1894:169586] 进入for循环,执行次数:1
2015-11-16 19:30:33.531 Demo04-GCD-Group[1894:169586] 进入for循环,执行次数:2
可以看出来只有一次...
利用一次性任务实现线程安全的单例.
下面用对比的方法来看安全与不安全的区别:
首先创建一个类DataCenter,对这个类设计单例方法,如下:
.h:
#import <Foundation/Foundation.h>
@interface DataCenter : NSObject
//API(Application Program Interface)
//线程不安全创建单例方法
+ (id)sharedDataCenterByUnsafe;
//线程安全创建单例方法
+ (id)sharedDataCenterBySafe;
//initialize方式创建创建单例对象(线程安全->了解)
+ (id)sharedDataCenterByInitialize;.m:
#import "DataCenter.h" @implementation DataCenter //线程不安全 static DataCenter *_dataCenter = nil; + (id)sharedDataCenterByUnsafe { if (_dataCenter == nil) { //确保两个线程同时执行到实例方法 [NSThread sleepForTimeInterval:1]; _dataCenter = [[TRDataCenter alloc] init]; } return _dataCenter; } //线程安全 + (id)sharedDataCenterBySafe { //1.声明一个空的静态的单例对象 static DataCenter *sharedDataCenter = nil; //2.声明一个静态的gcd单次任务对象 static dispatch_once_t onceToken; //3.执行gcd的单次任务,对对象进行初始化 dispatch_once(&onceToken, ^{ //初始化 //确保两个线程同时执行到实例方法 [NSThread sleepForTimeInterval:1]; sharedDataCenter = [[DataCenter alloc] init]; }); return sharedDataCenter; } //线程安全,听说第三方大多使用这种 static DataCenter *_dataCenterInit = nil; //重写initialize方法 + (void)initialize { if (self == [DataCenter class]) { //初始化操作 _dataCenterInit = [[DataCenter alloc] init]; } } + (id)sharedDataCenterByInitialize { return _dataCenterInit; }下面调用这些方法:
- (void)viewDidLoad { [super viewDidLoad]; //为了方便我就直接在这方法里调用了 [NSThread sleepForTimeInterval:1]; [self unsafeSingleton:nil]; [NSThread sleepForTimeInterval:3]; [self safeSingleton:nil]; } //需求: 验证两个对象的地址是否一样 - (IBAction)unsafeSingleton:(id)sender { //创建两个线程(NSThread) NSThread *firstThread = [[NSThread alloc] initWithTarget:self selector:@selector(getUnsafeSingleton) object:nil]; NSThread *secondThread = [[NSThread alloc] initWithTarget:self selector:@selector(getUnsafeSingleton) object:nil]; //启动线程 [firstThread start]; [secondThread start]; } - (void)getUnsafeSingleton { DataCenter *dataCenter = [DataCenter sharedDataCenterByUnsafe]; NSLog(@"线程不安全的单例对象地址:%p", dataCenter); } - (IBAction)safeSingleton:(id)sender { //创建两个线程对象 NSThread *firstThread = [[NSThread alloc] initWithTarget:self selector:@selector(getSafeSingleton) object:nil]; NSThread *secondThread = [[NSThread alloc] initWithTarget:self selector:@selector(getSafeSingleton) object:nil]; //启动 [firstThread start]; [secondThread start]; } - (void)getSafeSingleton { DataCenter *dataCenter = [DataCenter sharedDataCenterBySafe]; NSLog(@"线程安全的单例对象地址:%p", dataCenter); }执行结果:
2015-11-16 19:38:03.930 Demo01-Singleton[1948:174311] 线程不安全的单例对象地址:0x7fa862431e10
2015-11-16 19:38:03.930 Demo01-Singleton[1948:174312] 线程不安全的单例对象地址:0x7fa8625375a0
2015-11-16 19:38:06.931 Demo01-Singleton[1948:174316] 线程安全的单例对象地址:0x7fa86253aff0
2015-11-16 19:38:06.931 Demo01-Singleton[1948:174315] 线程安全的单例对象地址:0x7fa86253aff0
----------------------------------------华丽的分割线----------------------------------------
GCD 延迟执行:
直接上代码了:
- (void)viewDidLoad {
[super viewDidLoad];
[self delayTaskByPerform:nil];
[self delayTaskByGCD:nil];
}
- (IBAction)delayTaskByPerform:(id)sender {
//调用perform延迟方法(5s)
[self performSelector:@selector(excuteTask) withObject:nil afterDelay:5];
//实现延迟的任务逻辑
NSLog(@"执行完毕");
}
- (void)excuteTask {
//主线程
for (int i = 0; i < 10; i++) {
//阻塞主线程
[NSThread sleepForTimeInterval:1];
NSLog(@"++++++%@", [NSThread currentThread]);
}
}
- (IBAction)delayTaskByGCD:(id)sender {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//主线程执行
NSLog(@"延迟任务");
});
NSLog(@"延迟任务执行完毕");
}执行结果:
2015-11-16 19:51:02.422 Demo05-GCD-Once[2011:180834] 执行完毕
2015-11-16 19:51:02.423 Demo05-GCD-Once[2011:180834] 延迟任务执行完毕
2015-11-16 19:51:08.430 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:09.435 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:10.438 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:11.443 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:12.445 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:13.447 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:14.450 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:15.451 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{n
9bd0
umber = 1, name = main}
2015-11-16 19:51:16.452 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:17.454 Demo05-GCD-Once[2011:180834] ++++++<NSThread: 0x7fe279d07740>{number = 1, name = main}
2015-11-16 19:51:17.454 Demo05-GCD-Once[2011:180834] 延迟任务
总结:
第一种是[self performSelector: withObject: afterDelay:];
第二种是dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//主线程执行
//code
});
---------------------------------华丽的分割线---------------------------------
GCD 基本上的内容就差不多了.
从上面代码中也发现了回到主线程的方法了吧!!!
在子线程中使用dispatch_async(dispatch_get_mainqueue(),^{
//code
});讲了"老大",接下来讲"老二了"...
相关文章推荐
- Python3写爬虫(四)多线程实现数据爬取
- C#实现多线程的同步方法实例分析
- 浅谈chuck-lua中的多线程
- C#简单多线程同步和优先权用法实例
- C#多线程学习之(四)使用线程池进行多线程的自动管理
- C#多线程编程中的锁系统(三)
- C#多线程学习之(六)互斥对象用法实例
- 基于一个应用程序多线程误用的分析详解
- C#多线程学习之(三)生产者和消费者用法分析
- C#多线程学习之(一)多线程的相关概念分析
- C#多线程之Thread中Thread.IsAlive属性用法分析
- C#控制台下测试多线程的方法
- Ruby 多线程的潜力和弱点分析
- C#中WPF使用多线程调用窗体组件的方法
- C#如何对多线程、多任务管理(demo)
- C#实现多线程的Web代理服务器实例
- c#实现多线程局域网聊天系统
- PHP使用CURL实现多线程抓取网页
- 浅解关于C#多线程的介绍
- 批处理程序中的“多线程”处理代码