网络&多线程相关iOS 多线程

多线程第一弹 - GCD

2016-03-22  本文已影响107人  Alexander

前言

在开发过程中,我们会经常和网络打交道,与网络相关的知识无疑是iOS开发中的难点之一,学习多线程,我走了很多弯路,遇到过很多坑,不过对于我们来说,学习就是需要从一个坑爬出来,然后又跳到另一个坑.本篇文章主要介绍多线程相关的基本知识,想要深入了解,可以关注CocoaChina微信公众号,里面有很多大牛的技术博客.感谢@小笨狼Lc提供的相关资料.接下来我们正式开始我们的多线程之旅吧.

在正式接触多线程的核心知识前,我们需要了解一些与网络相关的基本知识.比如说:进程和线程分别代表的是什么意思. 两者有什么联系等.

进程与线程的基本概念

进程.png 线程.png

线程的串行与并行

串行执行.png 并行执行.png

多线程

iOS中多线程的实现方案

GCD

什么是GCD : GCD是Grand Central Disparch的简称,GCD可以自动管理线程的生命周期(创建线程, 调度线程以及销毁线程).所以我们不需要再去关注线程,只需要高数GCD执行的是什么任务以及以什么方式执行任务即可.

GCD的两个核心概念(任务和队列)

GCD的两个核心步骤:

先进先出原则.png
@property (nonatomic, strong) dispatch_queue_t queue;
#if OS_OBJECT_USE_OBJC
@property (nonatomic, strong) dispatch_queue_t queue;
#else
@property (nonatomic, assign) dispatch_queue_t queue;
#endif

GCD中的同步函数与异步函数

异步函数(async) : 可以在新线程上执行任务具备开启新线程的能力.
// 参数传的是一个block
dispatch_async(dispatch_queue_t queue, ^(void)block);

// 参数传的是一个指针函数
dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
异步函数示例
- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"异步函数之前 %@",[NSThread currentThread]);
    /**
     * 参数 1: 队列的名称  参数 2: 队列的类型
     * DISPATCH_QUEUE_SERIAL NULL  // 表示串行队列
     * DISPATCH_QUEUE_CONCURRENT  // 表示并发队列
     */
    dispatch_queue_t queue = dispatch_queue_create("我的微博是@WilliamAlex大叔", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{

        NSLog(@"异步函数中%@",[NSThread currentThread]);
    });

    NSLog(@"异步函数之后 %@",[NSThread currentThread]);
}
打印结果
2016-03-21 21:31:17.349 01 - 多线程[998:85748] 异步函数之前 <NSThread: 0x7fe800707430>{number = 1, name = main}
2016-03-21 21:31:17.350 01 - 多线程[998:85748] 异步函数之后 <NSThread: 0x7fe800707430>{number = 1, name = main}
2016-03-21 21:31:17.350 01 - 多线程[998:85848] 异步函数中<NSThread: 0x7fe80077c100>{number = 2, name = (null)}
同步函数 : 只能在当前线程上执行任务,不具备开启子线程的能力.
// 参数是block
dispatch_sync(dispatch_queue_t queue, ^(void)block);

// 参数是函数
dispatch_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work);

同步函数示例

- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"同步函数之前 %@",[NSThread currentThread]);
    /**
     * 参数 1: 队列的名称  参数 2: 队列的类型
     * DISPATCH_QUEUE_SERIAL NULL  // 表示串行队列
     * DISPATCH_QUEUE_CONCURRENT  // 表示并发队列
     */
    dispatch_queue_t queue = dispatch_queue_create("我的微博是@WilliamAlex大叔", DISPATCH_QUEUE_SERIAL);

    dispatch_sync(queue, ^{

        NSLog(@"同步函数中%@",[NSThread currentThread]);
    });

    NSLog(@"同步函数之后 %@",[NSThread currentThread]);
}
打印结果
2016-03-21 22:23:28.534 01 - 多线程[1037:95068] 同步函数之前 <NSThread: 0x7f8fd2601920>{number = 1, name = main}
2016-03-21 22:23:28.535 01 - 多线程[1037:95068] 同步函数中<NSThread: 0x7f8fd2601920>{number = 1, name = main}
2016-03-21 22:23:28.535 01 - 多线程[1037:95068] 同步函数之后 <NSThread: 0x7f8fd2601920>{number = 1, name = main}

线程死锁问题

死锁示例
- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"同步函数之前 %@",[NSThread currentThread]);
    /**
     * 参数 1: 队列的名称  参数 2: 队列的类型
     * DISPATCH_QUEUE_SERIAL NULL  // 表示串行队列
     * DISPATCH_QUEUE_CONCURRENT  // 表示并发队列
     */
    dispatch_queue_t queue = dispatch_queue_create("我的微博是@WilliamAlex大叔", DISPATCH_QUEUE_SERIAL);

        dispatch_async(queue, ^{
            NSLog(@"异步函数中%@",[NSThread currentThread]);
            dispatch_sync(queue, ^{

                NSLog(@"同步函数中%@",[NSThread currentThread]);
            });
        });

        NSLog(@"同步函数之后 %@",[NSThread currentThread]);
}

打印结果
2016-03-21 22:40:33.280 01 - 多线程[1112:100793] 同步函数之前 <NSThread: 0x7ff17a707510>{number = 1, name = main}
2016-03-21 22:40:33.281 01 - 多线程[1112:100793] 同步函数之后 <NSThread: 0x7ff17a707510>{number = 1, name = main}
2016-03-21 22:40:33.281 01 - 多线程[1112:100849] 异步函数中<NSThread: 0x7ff17a501380>{number = 2, name = (null)}

写到这里我们需要总结一下,否则以后我们只会越来越害怕多线程,害怕去接触网络相关的知识,做事情不能模棱两可,既然做了,就要弄明白.

下面我们具体举例

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"-------------begin");
    // 1, 获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
//    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);

    // 将任务添加到队列中
    dispatch_sync(mainQueue, ^{

        NSLog(@"1我的微博@WilliamAlex大叔%@",[NSThread currentThread]);

    });

    NSLog(@"-------------End");
}
}

打印结果

2016-03-22 10:39:17.524 01 - 多线程[655:22430] -------------begin

异步函数 + 主队列

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"-------------begin");
    // 1, 获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    // 将任务添加到队列中
    dispatch_async(mainQueue, ^{

        NSLog(@"哎呀!妈呀, 多线程的水很深啊%@",[NSThread currentThread]);
    });

    NSLog(@"-------------End");
}

打印结果

2016-03-22 10:44:01.294 01 - 多线程[668:23853] -------------begin
2016-03-22 10:44:01.294 01 - 多线程[668:23853] -------------End
2016-03-22 10:44:01.302 01 - 多线程[668:23853] 哎呀!妈呀, 多线程的水很深啊<NSThread: 0x7fe583700bd0>{number = 1, name = main}

同步函数 + 串行队列

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"-------------begin");
    // 1, 获取串行:DISPATCH_QUEUE_SERIAL 或 NULL即可
    dispatch_queue_t syncSerial = dispatch_queue_create("WilliamAlex大叔", DISPATCH_QUEUE_SERIAL);

    // 将任务添加到队列中
    dispatch_sync(syncSerial, ^{

        NSLog(@"1WilliiamAlex大叔%@",[NSThread currentThread]);
    });

    dispatch_sync(syncSerial, ^{

        NSLog(@"2WilliiamAlex大叔%@",[NSThread currentThread]);
    });

    dispatch_sync(syncSerial, ^{

        NSLog(@"3WilliiamAlex大叔%@",[NSThread currentThread]);
    });
    NSLog(@"-------------End");
}

打印结果

2016-03-22 10:52:32.478 01 - 多线程[703:27166] -------------begin
2016-03-22 10:52:32.478 01 - 多线程[703:27166] 1WilliiamAlex大叔<NSThread: 0x7fbb0b6075d0>{number = 1, name = main}
2016-03-22 10:52:32.479 01 - 多线程[703:27166] 2WilliiamAlex大叔<NSThread: 0x7fbb0b6075d0>{number = 1, name = main}
2016-03-22 10:52:32.479 01 - 多线程[703:27166] 3WilliiamAlex大叔<NSThread: 0x7fbb0b6075d0>{number = 1, name = main}
2016-03-22 10:52:32.479 01 - 多线程[703:27166] -------------End

异步函数 + 串行队列

- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"-------------begin");
    // 1, 获取串行:DISPATCH_QUEUE_SERIAL 或 NULL即可
    dispatch_queue_t syncSerial = dispatch_queue_create("WilliamAlex大叔", DISPATCH_QUEUE_SERIAL);

    // 将任务添加到队列中
    dispatch_async(syncSerial, ^{

        NSLog(@"1WilliiamAlex大叔%@",[NSThread currentThread]);
    });

    dispatch_async(syncSerial, ^{

        NSLog(@"2WilliiamAlex大叔%@",[NSThread currentThread]);
    });

    dispatch_async(syncSerial, ^{

        NSLog(@"3WilliiamAlex大叔%@",[NSThread currentThread]);
    });

    NSLog(@"-------------End");
}

打印结果

2016-03-22 10:57:45.433 01 - 多线程[716:28932] -------------begin
2016-03-22 10:57:45.434 01 - 多线程[716:28932] -------------End
2016-03-22 10:57:45.434 01 - 多线程[716:28983] 1WilliiamAlex大叔<NSThread: 0x7f7ff0c20060>{number = 2, name = (null)}
2016-03-22 10:57:45.434 01 - 多线程[716:28983] 2WilliiamAlex大叔<NSThread: 0x7f7ff0c20060>{number = 2, name = (null)}
2016-03-22 10:57:45.434 01 - 多线程[716:28983] 3WilliiamAlex大叔<NSThread: 0x7f7ff0c20060>{number = 2, name = (null)}

同步函数 + 并发队列

- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"-------------begin");
    // 1, 获取并发队列 : DISPATCH_QUEUE_CONCURRENT
//    dispatch_queue_t syncSerial = dispatch_queue_create("WilliamAlex大叔", DISPATCH_QUEUE_CONCURRENT);

    // 可以直接使用全局的并发队列(第一个0:表示系统默认即可. 第二个0:表示预留字段)
    dispatch_queue_t globalQuque = dispatch_get_global_queue(0, 0);

    // 将任务添加到队列中

    dispatch_sync(globalQuque, ^{

        for (NSInteger i = 0; i < 3; i++) {

            NSLog(@"1WilliiamAlex大叔%@",[NSThread currentThread]);
        }
    });

    dispatch_sync(globalQuque, ^{

        for (NSInteger i = 0; i < 3; i++) {

            NSLog(@"2WilliiamAlex大叔%@",[NSThread currentThread]);
        }
    });

    NSLog(@"-------------End");
}

打印结果

2016-03-22 11:13:23.988 01 - 多线程[794:35399] -------------begin
2016-03-22 11:13:23.989 01 - 多线程[794:35399] 1WilliiamAlex大叔<NSThread: 0x7fafea506970>{number = 1, name = main}
2016-03-22 11:13:23.989 01 - 多线程[794:35399] 1WilliiamAlex大叔<NSThread: 0x7fafea506970>{number = 1, name = main}
2016-03-22 11:13:23.990 01 - 多线程[794:35399] 1WilliiamAlex大叔<NSThread: 0x7fafea506970>{number = 1, name = main}
2016-03-22 11:13:23.990 01 - 多线程[794:35399] 2WilliiamAlex大叔<NSThread: 0x7fafea506970>{number = 1, name = main}
2016-03-22 11:13:23.990 01 - 多线程[794:35399] 2WilliiamAlex大叔<NSThread: 0x7fafea506970>{number = 1, name = main}
2016-03-22 11:13:23.990 01 - 多线程[794:35399] 2WilliiamAlex大叔<NSThread: 0x7fafea506970>{number = 1, name = main}
2016-03-22 11:13:23.990 01 - 多线程[794:35399] -------------End

异步函数 + 并发队列

- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"-------------begin");
    // 1, 获取并发队列 : DISPATCH_QUEUE_CONCURRENT
//    dispatch_queue_t syncSerial = dispatch_queue_create("WilliamAlex大叔", DISPATCH_QUEUE_CONCURRENT);

    // 可以直接使用全局的并发队列(第一个0:表示系统默认即可. 第二个0:表示预留字段)
    dispatch_queue_t globalQuque = dispatch_get_global_queue(0, 0);

    // 将任务添加到队列中

    dispatch_async(globalQuque, ^{

        for (NSInteger i = 0; i < 10; i++) {

            NSLog(@"1WilliiamAlex大叔%@",[NSThread currentThread]);
        }
    });

    dispatch_async(globalQuque, ^{

        for (NSInteger i = 0; i < 10; i++) {

            NSLog(@"2WilliiamAlex大叔%@",[NSThread currentThread]);
        }
    });

    NSLog(@"-------------End");
}

打印结果(部分打印)

2016-03-22 11:09:43.346 01 - 多线程[774:33864] -------------begin
2016-03-22 11:09:43.347 01 - 多线程[774:33864] -------------End
2016-03-22 11:09:43.347 01 - 多线程[774:33908] 2WilliiamAlex大叔<NSThread: 0x7fa45b43d030>{number = 2, name = (null)}
2016-03-22 11:09:43.347 01 - 多线程[774:33907] 1WilliiamAlex大叔<NSThread: 0x7fa45b61fcf0>{number = 3, name = (null)}
2016-03-22 11:09:43.348 01 - 多线程[774:33908] 2WilliiamAlex大叔<NSThread: 0x7fa45b43d030>{number = 2, name = (null)}
2016-03-22 11:09:43.348 01 - 多线程[774:33907] 1WilliiamAlex大叔<NSThread: 0x7fa45b61fcf0>{number = 3, name = (null)}
2016-03-22 11:09:43.348 01 - 多线程[774:33908] 2WilliiamAlex大叔<NSThread:
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work);

dispatch_barrier示例
- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"同步函数之前 %@",[NSThread currentThread]);
    /**
     * 参数 1: 队列的名称  参数 2: 队列的类型
     * DISPATCH_QUEUE_SERIAL NULL  // 表示串行队列
     * DISPATCH_QUEUE_CONCURRENT  // 表示并发队列
     */
    dispatch_queue_t queue = dispatch_queue_create("我的微博是@WilliamAlex大叔", DISPATCH_QUEUE_CONCURRENT);

        dispatch_sync(queue, ^{

         NSLog(@"同步函数中%@",[NSThread currentThread]);
        });

    dispatch_barrier_async(queue, ^{

        NSLog(@"猜猜我在哪里执行%@",[NSThread currentThread]);
    });

        NSLog(@"同步函数之后 %@",[NSThread currentThread]);

}

打印结果
2016-03-21 23:44:55.517 01 - 多线程[1196:116016] 同步函数之前 <NSThread: 0x7ffea8c02a00>{number = 1, name = main}
2016-03-21 23:44:55.517 01 - 多线程[1196:116016] 同步函数中<NSThread: 0x7ffea8c02a00>{number = 1, name = main}
2016-03-21 23:44:55.517 01 - 多线程[1196:116016] 同步函数之后 <NSThread: 0x7ffea8c02a00>{number = 1, name = main}
2016-03-21 23:44:55.518 01 - 多线程[1196:116084] 猜猜我在哪里执行<NSThread: 0x7ffea8e5ba80>{number = 2, name = (null)}

知识拓展
- (void)setObject:(id)anObject forKey:(id
)aKey
{
    dispatch_barrier_async(self.concurrentQueue, ^{
        [self.mutableDictionary setObject:anObject forKey:aKey];
    });
}

- (id)objectForKey:(id)aKey
{
    __block id object = nil;
    dispatch_sync(self.concurrentQueue, ^{
        object = [self.mutableDictionary objectForKey:aKey];
    });    return  object;
}

创建队列

/*
    参数 1: 队列的名称
    参数 2: 队列的类型
    * NULL                         // 表示串行队列
    * DISPATCH_QUEUE_SERIAL       // 表示串行队列
    * DISPATCH_QUEUE_CONCURRENT  // 表示并发队列
*/
    dispatch_queue_t queue = dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

使用dispatch_queue_create函数创建串行队列
// 创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)

// 方式 1 : NULL
 dispatch_queue_t queue = dispatch_queue_create(@"com.William.Alex", NULL);

// 方式 2 : DISPATCH_QUEUE_SERIAL
 dispatch_queue_t queue = dispatch_queue_create(@"com.William.Alex", DISPATCH_QUEUE_SERIAL);

使用主队列(跟主线程相关联的队列)
主队列是GCD自带的一种特殊的串行队列
放在主队列中的任务,都会放到主线程中执行
使用dispatch_get_main_queue()获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();

 dispatch_queue_t queue = dispatch_queue_create(@"com.William.Alex", DISPATCH_QUEUE_CONCURRENT);
GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建
使用dispatch_get_global_queue函数获得全局的并发队列
dispatch_queue_t dispatch_get_global_queue(
dispatch_queue_priority_t priority, // 队列的优先级
unsigned long flags); // 用0即可

获得全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

全局并发队列的优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台

知识拓展

// 直接获取主队列
NSLog(@"主队列上的任务都是在主线程上执行的%@",dispatch_get_main_queue());
主队列的使用场景
dispatch_async(dispatch_get_main_queue(), ^{
    // 刷新UI界面操作
});
很多时候我们喜欢将0或者NULL传入作为参数

dispatch_get_global_queue(NULL, NULL)

由于NULL等于0,也就是DISPATCH_QUEUE_PRIORITY_DEFAULT,所以返回的是默认优先级

网络延迟

dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
void dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *context, dispatch_function_t work);
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(run) userInfo:nil repeats:YES];

// 直接调用NSObject方法,,performeSelector方法也是比较常见具体实现是.
// nonnull : 表示参数不能设置为空
// nullable : 表示参数可以设置为空
[self performSelector:(nonnull SEL) withObject:(nullable id) afterDelay:(NSTimeInterval)]

[self performSelector:(nonnull SEL) withObject:(nullable id) afterDelay:(NSTimeInterval) inModes:(nonnull NSArray<NSString *> *)]
/*
#define DISPATCH_TIME_NOW (0ull)
#define DISPATCH_TIME_FOREVER (~0ull)
注意 : 不能传DISPATCH_TIME_FOREVER,会一直阻塞线程
*/
dispatch_after(dispatch_time_t when, dispatch_queue_t queue, ^(void)block)

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //  2.0
    });

总结延迟操作

dispatch_queue_set_specific 和 dispatch_queue_get_specific

dispatch_queue_set_specific的使用场景

void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, void *context, dispatch_function_t destructor);

参数说明

dispatch_queue_get_specific的使用场景

void *dispatch_queue_get_specific(dispatch_queue_t queue, const void *key);
void *dispatch_get_specific(const void *key);

dispatch_queue_get_specific的解释

一个人的力量是有限的,如果文章有有错误,希望大家指出来,相互帮助,相互进步.感谢@小笨狼Lc提供资料,想要了解更多知识可以点进连接,关注CocoaChina的微信公众号.http://www.cocoachina.com/ios/20160225/15422.html
后续会持续更新.....加油!!!!!

上一篇下一篇

猜你喜欢

热点阅读