美文网首页
iOS-多线程01-基本认识和使用

iOS-多线程01-基本认识和使用

作者: IBigLiang | 来源:发表于2019-09-27 19:13 被阅读0次

首先,我们先来看看,在iOS领域中,常用的多线程方案,大致上有哪几种。基本上,我们最常用的应该是GCD、NSThread和NSOperation这三种,除此之外,还有一种不是很常用的pthread,这次,我们可以用一个表格来总结多线程的几种方案以及他们各自的特点:


image.png

在这里,我们就针对最常用的GCD,来看看多线程的使用。首先,我们需要了解的是,GCD的执行任务方式有两种:就是async(异步)和sync(同步)。以下是两种模式的执行代码定义:

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);  // 异步模式
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block); // 同步模式

这里同步和异步的函数都有两个参数,一是队列(queue),而是任务(block)。
任务其实很好理解,它就是一个执行代码的block。这里我们需要掌握的是Queue对象这个概念。首先,这里的队列有两种,并行和串行。从字面意思大家就知道,并行就是一起进行,串行就是单个进行。然后我们把队列和任务结合在一起讲就是,并行队列里面的任务,是一起执行的,基本不分先后;而串行队列的任务是依次执行的。
然后我们再回过头去看看dispatch_async、dispatch_sync这两个GCD的函数,首先大家需要知道,这两个同步和异步的函数,它们主要的区别(或者说是对程序的影响),在于是否能够开启新的线程。
1、同步:在当前线程中执行任务,当然不会也不需要再去开启一个新的线程。
2、异步:在新的线程中执行任务,具备开启新线程的能力。
接下来,我们还是用代码的方式来观察下同步、异步、串行、并行:

/**
 异步串行
 */
- (void)async_serial {
    
    dispatch_queue_t queue = dispatch_queue_create("new", DISPATCH_QUEUE_SERIAL); // 串行队列
    
    dispatch_async(queue, ^{
        NSLog(@"1 - %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"2 - %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"3 - %@", [NSThread currentThread]);
    });
}
// 打印如下:
2019-09-03 17:41:20.025056+0800 001-多线程的理解以及常规使用[24993:30713871] 1 - <NSThread: 0x600001d4cdc0>{number = 3, name = (null)}
2019-09-03 17:41:20.025377+0800 001-多线程的理解以及常规使用[24993:30713871] 2 - <NSThread: 0x600001d4cdc0>{number = 3, name = (null)}
2019-09-03 17:41:20.025503+0800 001-多线程的理解以及常规使用[24993:30713871] 3 - <NSThread: 0x600001d4cdc0>{number = 3, name = (null)}

可以看到,异步串行,开启了新的线程,但是串行一次执行任务
/**
 异步并行
 */
- (void)async_concurrent {
    
    dispatch_queue_t queue = dispatch_queue_create("new", DISPATCH_QUEUE_CONCURRENT); // 并行队列
    
    dispatch_async(queue, ^{
        NSLog(@"1 - %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"2 - %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"3 - %@", [NSThread currentThread]);
    });
}
// 打印如下:
2019-09-03 17:44:18.598563+0800 001-多线程的理解以及常规使用[25051:30717738] 2 - <NSThread: 0x600001a68700>{number = 4, name = (null)}
2019-09-03 17:44:18.598564+0800 001-多线程的理解以及常规使用[25051:30717741] 3 - <NSThread: 0x600001a5c500>{number = 5, name = (null)}
2019-09-03 17:44:18.598598+0800 001-多线程的理解以及常规使用[25051:30717739] 1 - <NSThread: 0x600001a6cf80>{number = 3, name = (null)}

可以看到,异步并行,开启了新的线程,而且是没有顺序的执行任务
/**
 同步串行
 */
- (void)sync_serial {
    
    dispatch_queue_t queue = dispatch_queue_create("new", DISPATCH_QUEUE_SERIAL); // 串行队列
    
    dispatch_sync(queue, ^{
        NSLog(@"1 - %@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"2 - %@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"3 - %@", [NSThread currentThread]);
    });
}
// 打印如下:
2019-09-03 17:46:10.533988+0800 001-多线程的理解以及常规使用[25089:30721269] 1 - <NSThread: 0x600001b20940>{number = 1, name = main}
2019-09-03 17:46:10.534154+0800 001-多线程的理解以及常规使用[25089:30721269] 2 - <NSThread: 0x600001b20940>{number = 1, name = main}
2019-09-03 17:46:10.534256+0800 001-多线程的理解以及常规使用[25089:30721269] 3 - <NSThread: 0x600001b20940>{number = 1, name = main}

可以看到,同步串行,没有开启新的线程,是在当前主线程下,依次执行任务
/**
 同步并行
 */
- (void)sync_concurrent {
    
    dispatch_queue_t queue = dispatch_queue_create("new", DISPATCH_QUEUE_CONCURRENT); // 并行队列
    
    dispatch_sync(queue, ^{
        NSLog(@"1 - %@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"2 - %@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"3 - %@", [NSThread currentThread]);
    });
}
// 打印如下:
2019-09-03 17:47:31.950982+0800 001-多线程的理解以及常规使用[25122:30723769] 1 - <NSThread: 0x600001bad380>{number = 1, name = main}
2019-09-03 17:47:31.951118+0800 001-多线程的理解以及常规使用[25122:30723769] 2 - <NSThread: 0x600001bad380>{number = 1, name = main}
2019-09-03 17:47:31.951238+0800 001-多线程的理解以及常规使用[25122:30723769] 3 - <NSThread: 0x600001bad380>{number = 1, name = main}

可以看到,同步并行,没有开启新的线程,是在当前主线程下,依次执行任务

最后一个特别的点,就是在主队列中添加任务,并且在同步和异步的模式下执行

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"1 - %@", [NSThread currentThread]);
    });
    
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2 - %@", [NSThread currentThread]);
    });
    
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"3 - %@", [NSThread currentThread]);
    });
}

首先是在主线程中同步添加主队列,这种方式,会导致死锁,原因就是本身viewDidLoad就是主队列的任务,然后在这个任务中添加同步主队列任务的话,会形成相互等待,导致死锁。
其实,总结一点就是,使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)
- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"1 - %@", [NSThread currentThread]);
        });
        
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"2 - %@", [NSThread currentThread]);
        });
        
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"3 - %@", [NSThread currentThread]);
        });
    });
}
// 打印如下:
2019-09-04 17:34:31.971216+0800 001-多线程的理解以及常规使用[46330:33540097] 1 - <NSThread: 0x600003721380>{number = 1, name = main}
2019-09-04 17:34:31.972003+0800 001-多线程的理解以及常规使用[46330:33540097] 2 - <NSThread: 0x600003721380>{number = 1, name = main}
2019-09-04 17:34:31.972770+0800 001-多线程的理解以及常规使用[46330:33540097] 3 - <NSThread: 0x600003721380>{number = 1, name = main}
可以看到,在子队列中,同步添加主队列任务,是在当前主线程下,依次执行任务
- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"1 - %@", [NSThread currentThread]);
        });
        
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"2 - %@", [NSThread currentThread]);
        });
        
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"3 - %@", [NSThread currentThread]);
        });
    });
}
// 打印如下:
2019-09-04 17:38:09.383051+0800 001-多线程的理解以及常规使用[46419:33546730] 1 - <NSThread: 0x600002c82940>{number = 1, name = main}
2019-09-04 17:38:09.383188+0800 001-多线程的理解以及常规使用[46419:33546730] 2 - <NSThread: 0x600002c82940>{number = 1, name = main}
2019-09-04 17:38:09.383281+0800 001-多线程的理解以及常规使用[46419:33546730] 3 - <NSThread: 0x600002c82940>{number = 1, name = main}
可以看到,在子队列中,异步添加主队列任务,是在当前主线程下,依次执行任务

由上述,我们可以得到如下结论:


image.png

最后,我们来看看队列组的使用

dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("new", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"111");
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"222");
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"333");
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"444");
    });
// 打印如下:
2019-09-04 17:43:37.865546+0800 001-多线程的理解以及常规使用[46513:33554033] 111
2019-09-04 17:43:37.865558+0800 001-多线程的理解以及常规使用[46513:33554031] 333
2019-09-04 17:43:37.865550+0800 001-多线程的理解以及常规使用[46513:33554038] 222
2019-09-04 17:43:37.865739+0800 001-多线程的理解以及常规使用[46513:33554033] 444
队列组,就是完成dispatch_group_async中所有的任务之后,最后返回到dispatch_group_notify中,其实就是dispatch_group_notify这个函数在监听队列组中的任务的完成情况,一旦完成,则会去执行dispatch_group_notify函数中的任务。

多线程的基本使用,先说到这里,接下来一篇文章,笔者准备分享多线程的安全隐患,以及一些常用的解决方案(包含iOS中常用的几大锁的类型),如下:

OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semaphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized

下一篇文章见!

相关文章

  • iOS-多线程01-基本认识和使用

    首先,我们先来看看,在iOS领域中,常用的多线程方案,大致上有哪几种。基本上,我们最常用的应该是GCD、NSThr...

  • 012-线程,生产消费模式,线程的通讯

    多线程基础 进程和线程 多线程的基本实现 使用Thread类 基本实现: 实现售票业务: 使用Runnable接口...

  • 多线程和AFN网络框架配合使用

    ios的多线程一般有NSOperation和GCD.NSOperation基本使用: GCD基本使用: 简单的多线...

  • iOS GCD的基本使用

    GCD在iOS中多线程开发中使用频繁,使用方便简单,可以满足我们大部分需求。其使用方法如下: 1、基本认识 GCD...

  • iOS底层原理——浅谈多线程

    iOS-浅谈多线程 一、造成线程堵塞的条件: 1、当前串行队列有任务。2、在这个任务中使用同步函数给当前串行队列添...

  • iOS多线程之NSThread

    前面总结了多线程基本概念和iOS多线程PThread的使用,下面接着总结iOS多线程的另外一种实现方案NSThre...

  • 05-多线程(1)

    0708-GCD单例模式 1、概述(01-多线程的基本概念) 多线程如果掌握得不好对一些性能方面的东西会做的不好...

  • 多线程(二)

    上篇多线程(一)我们基本看了多线程的使用,下面我们再来看看具体的使用和一些需要注意的点代码详见 gitHub_D...

  • localstorage的认识和基本使用

    1.localstorage是什么以及主要用途? 在HTML5中,新加入了一个localStorage特性,这个特...

  • iOS 多线程简单总结之GCD

    iOS中常用的多线程方案有GCD和NSOperation,至于NSThread和pthread基本不使用,下面简单...

网友评论

      本文标题:iOS-多线程01-基本认识和使用

      本文链接:https://www.haomeiwen.com/subject/lwnhyctx.html