您的位置:首页 > 移动开发 > IOS开发

总结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:
#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
});讲了"老大",接下来讲"老二了"...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息