总结iOS 多线程学习过程五

 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>{number = 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
});
讲了"老大",接下来讲"老二了"...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值