zoukankan      html  css  js  c++  java
  • iOS中的多线程基础

    NSThread

    NSThread是一个苹果封装过的,面向对象的线程对象。但是它的生命周期需要我们自己来手动管理,所以使用不是很常见,比如[NSThread currentThread],它可以获取当前的线程类,你就可以知道当前线程的各种属性。

    创建并启动

        //1.先创建,再启动
        //创建
        NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
        //启动
        [thread start];
        
        //2.创建并自动启动
        [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
        //苹果认为performSelector系列方法不安全,所以在swift中,去掉了这个方法
        [self performSelectorInBackground:@selector(run) withObject:nil];

    NSThread其他常用的方法

        /*
        //3.NSThread的常见方法
        //取消线程
        - (void)cancel;
        //启动线程
        - (void)start;
        //判断某个线程的状态的属性
        @property (readonly, getter=isExecuting) BOOL executing;
        @property (readonly, getter=isFinished) BOOL finished;
        @property (readonly, getter=isCancelled) BOOL cancelled;
        //设置和获取线程名字
        -(void)setName:(NSString *)n;
        -(NSString *)name;
        //获取当前线程信息
        + (NSThread *)currentThread;
        //获取主线程信息
        + (NSThread *)mainThread;
        //使当前线程暂停一段时间,或者暂停到某个时刻
        + (void)sleepForTimeInterval:(NSTimeInterval)time;
        + (void)sleepUntilDate:(NSDate *)date;
         */

    GCD

    Grand Central Dispatch,感觉很强大,当然,它的确很强大。它是苹果公司为多处理器的并行运算提出的解决方案,所以可以自动合理的利用跟多的cpu内核(不懂~~~),GCD可以自动的管理线程的生命周期(创建线程,调度任务,销毁线程),作为程序员,我们不需要管理线程的生命周期,我们只需要告诉GCD我们需要完成什么任务。GCD使用了很多的Block(闭包),所以使用起来也很方便,基本上GCD的使用是比较多的。

    任务和队列

    概念:

    任务

      任务就是操作,你要完成什么任务,就是你要完成什么操作,也就是一段代码。这段代码可以写到GCD中的Block中,所以这样添加任务特别方便。

      任务有两种执行方式:同步执行和异步执行的主要区别是会不会阻塞当前线程,知道Block中的代码执行完毕。

    如果是同步(sync)操作,它会阻塞当前线程并等待Block中的任务执行完毕,然后当前线程才能继续执行。

    如果是异步(async)操作,当前线程会继续往下执行,它不会阻塞当前线程。

    队列

      队列是用来存放任务的。队列分为两种,串行队列和并行队列。

    • 串行队列

      串行队列中的任务会根据队列的定义FIFO的执行,一个接一个的先进先出进行执行。放到串行队列中的任务,GCD会根据FIFO(先进先出)地取出来一个,执行一个,然后去下一个,执行下一个,这样一个接一个的执行。

    • 并行队列

      并行队列中的任务会根据同步和异步的不同有不同的执行方式。

    并行队列 中的任务根据同步或异步有不同的执行方式。虽然很绕,但请看下表

    blob.png

    在串行队列中执行同步任务:不会新建线程,按顺序执行任务(毫无用处)
    在串行队列中执行异步任务,会新建线程,按顺序执行任务(非常有用)

    在并行队列中执行同步任务:不会新建线程,按顺序执行任务(几乎没用)
    在并行队列中执行异步任务:会新建多个线程,但是无法确定任务的执行顺序(有用,但是很容易出错)
     

    创建队列和创建任务

     
        //GCD
        //创建队列
        //主队列,是一个特殊的串行队列,它用于刷新UI,如何需要刷新UI的工作都要在主队列中进行,所以一般耗时的任务都要放在别的线程中完成。
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        //自己创建的队列:自己可以创建串行队列,也可以创建并行队列。其中第一个参数是标识符,用于DEBUG的时候标识唯一的队列,可以为空。第二个参数很重要,用来表示创建的队列是并行的还是串行的,传入DIS或者NULL表示创建了一个串行队列。传入DIS表示创建了一个并行队列。
        dispatch_queue_t queue2 = dispatch_queue_create("dl.test.QueueTest2", DISPATCH_QUEUE_SERIAL);
        
        dispatch_queue_t queue3 = dispatch_queue_create("dl.test.QueueTest3", DISPATCH_QUEUE_CONCURRENT);
        
        //创建任务
        //创建同步任务:不会另开线程(SYNC)
        dispatch_sync(queue2, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
        
        //异步任务:会另开线程(ASYNC)
        dispatch_async(queue3, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });

    这里我们看两个例子,对于GCD中,同步任务的使用问题:

        NSLog(@"之前-%@", [NSThread currentThread]);
    
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"sync - %@", [NSThread currentThread]);
        });
        
        NSLog(@"之后-%@", [NSThread currentThread]);
        
        /*
            分析:
         1.打印完第一句后,dispatch_sync 就会立即阻塞当前的主线程,然后把block中的任务放到main_queue中,可是main_queue中的任务会被取出来放到主线程中执行,但是,主线程现在已经被dispatch_sync阻塞了,所以block中的代码就不能完成,不能完成,dispatch_sync就会一直阻塞主线程,造成死锁现象,导致主线程卡死。
         */

    第二个例子

    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
        NSLog(@"之前-%@",[NSThread currentThread]);
        dispatch_async(queue, ^{
            NSLog(@"sync之前-%@",[NSThread currentThread]);
            dispatch_sync(queue, ^{
                NSLog(@"sync-%@",[NSThread currentThread]);
            });
            NSLog(@"sync之后-%@",[NSThread currentThread]);
        });
        NSLog(@"之后-%@",[NSThread currentThread]);
        /*
            分析:
         1.首先创建一个叫做“myQueue”的串行队列。
         2.打印当前线程
         3.在myQueue的串行队列中添加异步任务(async),异步执行,不会阻塞当前线程,于是有了两条线程,一个线程继续打印“之后-%@”这句。另一个线程执行执行block中的内容,首先打印“sync之前-%@”
         4.接下来,dispatch_sync是同步任务,它会阻塞当前所在线程,一直等到sync里的任务执行完成后才会继续往下。于是 sync 就高兴的把自己 Block 中的任务放到 queue 中,可谁想 queue 是一个串行队列,一次执行一个任务,所以 sync 的 Block 必须等到前一个任务执行完毕,可万万没想到的是 queue 正在执行的任务就是被 sync 阻塞了的那个。于是又发生了死锁。
         */

    队列组

    将很多的队列添加到一个组里,当组里的所有任务都执行完成后,队列组会调用方法通知我们。

        //1.创建队列组
        dispatch_group_t group = dispatch_group_create();
        //2.创建队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        //3.多次使用队列组的方法执行任务,只有异步方法
        //3.1 执行3次循环
        dispatch_group_async(group, queue, ^{
            for (NSInteger i = 0; i < 3; i++) {
                NSLog(@"group-01 - %@", [NSThread currentThread]);
            }
        });
        //3.2 主队列执行8次循环
        dispatch_group_async(group, queue, ^{
            for (NSInteger i = 0; i < 8; i++) {
                NSLog(@"group-02 - %@", [NSThread currentThread]);
            }
        });
        //3.3 执行5次循环
        dispatch_group_async(group, queue, ^{
            for (NSInteger i = 0; i < 5; i++) {
                NSLog(@"group-03 - %@", [NSThread currentThread]);
            }
        });
        //4.都执行完成后自动通知
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"完成 - %@", [NSThread currentThread]);
        });

    回到主线程

    在其他线程中操作完成后,必须回到主线程中更新UI,下边代码是回到主线程的方法:

    GCD

    //Objective-C
    dispatch_async(dispatch_get_main_queue(), ^{
    });
  • 相关阅读:
    10. Regular Expression Matching
    9. Palindrome Number (考虑负数的情况)
    8. String to Integer (整数的溢出)
    7. Reverse Integer (整数的溢出)
    LeetCode Minimum Size Subarray Sum
    LeetCode Course Schedule II
    Linux 文件缓存 (一)
    LeetCode Tries Prefix Tree
    Linux : lsof 命令
    LeetCode Binary Tree Right Side View
  • 原文地址:https://www.cnblogs.com/iOSDeng/p/5075941.html
Copyright © 2011-2022 走看看