iOS中多线程的使用

Posted goodmorningmr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS中多线程的使用相关的知识,希望对你有一定的参考价值。

1.线程安全出现条件:多个线程访问更改同一个变量

2.OC在定义属性时有nonatomic和atomic两种选择

  • atomic:原子属性,在setter方法中会为属性加锁(默认为atomic),线程安全,需要消耗大量的资源
  • nonatomic:非原子属性,不会为setter方法加锁,非线程安全,适合内存较小的移动设备

ios开发建议:

  • 所有属性都声明为nonatomic
  • 尽量避免多条线程抢夺同一块资源
  • 精良将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

GCD(Grand Central Dispatch 中枢调度器)

  • GCD是苹果公司为多核的并行运算提出的解决方案
  • GCD会自动利用更多的CPU内核
  • GCD会自动管理线程的声明周期
  • 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

任务和队列

GCD的两个核心

  • 任务:执行什么操作
  • 队列:存放任务

将任务添加到队列

  • GCD会自动将队列中的任务取出,放到对应的线程中执行
  • 热舞的取出遵循队列的FIFO原则: 先进先出,后进后出

同步和异步的区别

  • 同步:只能在当前线程中执行,不具备开启新线程的能力
  • 异步:可以在新的线程中执行任务,具备开启新县城的能力(并非一定开启新线程)

队列的类型

  • 并发队列:
    • 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
    • 并发功能只有在异步(dispatch_async)函数下才有效异步(dispatch_async)函数下才有效
  • 串行队列:
    • 让任务一个接一个地执行(一个任务执行完毕后再执行下一个任务)
  • 队列的创建
dispatch_queue_t concurrentQueue = dispatch_queue_create(
"concurrentQueue",           //传递的字符串参数来标记线程
DISPATCH_QUEUE_CONCURRENT);  //参数包括DISPATCH_QUEUE_SERIAL (串行队列)
                                        DISPATCH_QUEUE_CONCURRENT (并行队列)

小结:

  • 同步和异步主要影响:任务的执行方式
  • 并发:允许多个任务并发(同时)执行
  • 串行:一个任务执行完毕后,再执行下一个任务

并发队列

  • GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建
    • 使用dispatch_get_global_queue函数获得全局的并发队列
    dispatch_get_global_queue(
    dispatch_queue_priority_t priority,     //队列的优先级
    unsigned long flags);     //此参数暂时无用,用0即可

串行队列

  • GCD中获得串行有两种途径
    • 使用dispatch_queue_create函数创建串行队列
      //创建串行队列(队列类型传递NULL或者DISPATCH_ QUEUE_SERIAL)
  • 主队列的使用
    • 主队列是GCD自带的一种特殊的串行队列
    • 放在主队列中的任务都会在主线程中执行
    • 使用dispatch_ get_ main_ queue()获得主队列
    • 异步函数正常情况下会开线程,但当异步函数和主线程结合时异步函数并不会开启新的线程,eg:在下面的代码执行中输出为{number = 1, name = main},由此可见当主线程与异步函数结合时并没有开启新的线程,并且执行方式为串行执行
dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(mainQueue, ^{
        NSLog(@"1----%@", [NSThread currentThread]);
    });
    dispatch_async(mainQueue, ^{
        NSLog(@"2----%@", [NSThread currentThread]);
    });
    dispatch_async(mainQueue, ^{
        NSLog(@"3----%@", [NSThread currentThread]);
    });
    dispatch_async(mainQueue, ^{
        NSLog(@"4----%@", [NSThread currentThread]);
    });
    dispatch_async(mainQueue, ^{
        NSLog(@"5----%@", [NSThread currentThread]);
    });
  • 线程死锁
    • 线程死锁条件
      • 调用方法的所在的队列与添加任务的队列为同一队列
      • 添加的任务为同步执行函数(同步执行即当前任务未执行完不执行下一个任务)
    • 原因:代码如下,我们在线程中调用lock方法时线程会将该方法添加到队列中,当我们在同一线程调用同步函数添加任务时该函数会等待队列中的lock方法执行完毕再添加任务,但是添加同步执行函数的方法又存在于lock方法内部,不添加任务该方法就无法完成,从而形成无法添加任务并且该方法也无法执行结束的局面,形成线程死锁。在主线程中调用下面的方法就会引起线程死锁
- (void)lock
{
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    NSLog(@"开启任务");
    dispatch_sync(mainQueue, ^{
        NSLog(@"1----%@", [NSThread currentThread]);
    });
    dispatch_sync(mainQueue, ^{
        NSLog(@"2----%@", [NSThread currentThread]);
    });
    dispatch_sync(mainQueue, ^{
        NSLog(@"3----%@", [NSThread currentThread]);
    });
    dispatch_sync(mainQueue, ^{
        NSLog(@"4----%@", [NSThread currentThread]);
    });
    dispatch_sync(mainQueue, ^{
        NSLog(@"5----%@", [NSThread currentThread]);
    });
}

注:线程死锁锁的到底是什么

分析在主线程调用下面代码是否会产生死锁

- (void)lock
{
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"当前线程----%@", [NSThread currentThread]);
    dispatch_sync(serialQueue, ^{
        NSLog(@"1----%@", [NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"2----%@", [NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"3----%@", [NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"4----%@", [NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"5----%@", [NSThread currentThread]);
    });
    NSLog(@"end");
}

在上面的代码调用方法在主线程,而且代码为同步执行,运行输出结果也在主线程,但在实际的运行中并没有出现死锁的现象,这是因为代码中lock所在的队列和lock中添加的任务队列并不是同一个队列,所以并不会出现任务相互等待的现象。换句话说上面描述的线程死锁是因为同一队列中任务的相互等待而造成的。

线程运行结果

  • 同步串行/异步串行执行的线程:在如下代码中,添加到线程中的任务会依次执行
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1.当前线程----%@", [NSThread currentThread]);
    dispatch_sync(serialQueue, ^{
        NSLog(@"2----%@", [NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"3----%@", [NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"4----%@", [NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"5----%@", [NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"6----%@", [NSThread currentThread]);
    });
    NSLog(@"end");

因为均为同步函数,输出依次执行, 根据异步执行的特点不难分析出异步执行的结果为1、end、2、3、4、5;

  • 异步串行掺杂同步串行
 dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1.当前线程----%@", [NSThread currentThread]);
    dispatch_async(serialQueue, ^{
        NSLog(@"2----%@", [NSThread currentThread]);
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"3----%@", [NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"4----%@", [NSThread currentThread]);
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"5----%@", [NSThread currentThread]);
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"6----%@", [NSThread currentThread]);
    });
    NSLog(@"end");
    

在上面的代码中2、3、5、6任务为异步执行,4的任务为同步执行,输出结果为1、2、3、4、end、5、6;并且其执行的线程也会发生变化。这是由于在方法执行中首先将2、3、4函数添加到队列中,但是4方法为同步执行方法所以其会等待队列中的任务执行完毕后再往下执行,并且线程的同步执行任务并不会开启新的线程,所以4方法回到主线程中执行,2、3、5、6为异步执行,与4方法不在同一线程。5、6为异步执行,添加到队列后并不会立即执行,所以输出在end之后。

栅栏函数

  • 在异步并行多任务执行的队列中,当我们希望前面的任务完成后再执行后面的任务时可使用栅栏函数来实现这个功能。
dispatch_queue_t queue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"1--- %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2--- %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"3--- %@", [NSThread currentThread]);
    });
    
    //栅栏函数
    dispatch_barrier_async(queue, ^{
        NSLog(@"------上面的已经执行完毕------");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"4--- %@", [NSThread currentThread]);
    });

注:栅栏函数不适用于全局并发队列(dispatch_ globle_queue)

dispatch_ async_ f的使用

dispatch_ async_ f和dispatch_ async功能相同,只是调用方式不同,dispatch_ async_f的使用如下

dispatch_queue_t globleQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async_f(globleQueue, NULL, tast);
    
void tast(void *param)
{
    NSLog(@"%s --- %@", __func__, [NSThread currentThread]);
 }

以上是关于iOS中多线程的使用的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发中多线程基础

iOS中多线程的使用

iOS 中多线程的简单使用

iOS中多线程的实现方案

iOS设计中多线程的简单介绍

iOS中多线程知识总结