zoukankan      html  css  js  c++  java
  • iOS开发之多线程编程

    iOS开发之多线程编程

    1. 多线程简述

    1.1什么是多线程? 解决的问题?

      多线程是指,编程中在主线程之外开辟的新线程,用于处理一些耗时的、并发的任务。使用多线程可以避免主线程的阻塞,也对一个线程不容易实现的任务提供了思路。在多线程的知识中也涉及队列,锁等概念。

      在这里科普一下队列的概念,队列:是管理线程的,相当于线程池,能管理线程什么时候执行。队列分为串行队列和并行队列。

      串行队列:队列中的线程按顺序执行(不会同时执行)

      并行队列:队列中的线程会并发执行,可能会有一个疑问,队列不是先进先出吗,如果后面的任务执行完了,怎么出去的了。这里需要强调下,任务执行完毕了,不一定出队列。只有前面的任务执行完了,才会出队列。

    2.1在iOS开发中我们可能会设计下面几种多线程的使用方式:

        (1)pthred多线程(POSIX标准),用在类unix系统上。

        (2)NSThread   是对pthred面相对象封装

        (3)NSOperation/NSOperationQueue   是使用GCD实现的一套Objective-C的API

        (4)GCD(Grand Central Dispatch)   是基于C语言的底层API

          使用较多,因为:1.支持多核心 2.c和block接口,易使用 3.功能强

    2.NSThread的使用

    2.1.创建方式2种

        //并行执行(多个任务同时执行)
        //创建新的线程(第一种方式) -- 对应一个方法
        //创建之后不会立即执行,需要start
        NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(taskA) object:nil];
        [thread1 start];
        
        
        //创建新的线程(方式2)--创建并立即执行
        [NSThread detachNewThreadSelector:@selector(taskB:) toTarget:self withObject:thread1];

    taskA,taskB为线程启动后要执行的方法

    2.2.可以使用通知的方式监听线程的结束

    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(dealThreadExit) name:NSThreadWillExitNotification object:nil];

    NSThreadWillExitNotification是一个系统通知,如果要监听某个线程的结束,可以在线程结束位置post一个通知,再用通知名监听线程结束。

    2.3.线程的通信和控制

    在下面的代码中,由createThread方法创建两个线程,分别执行taskA和taskB。taskA和taskB分别执行在不同的子线程中,在taskB方法中传入thread1的引用,在taskB运行到一定时刻,控制taskA所在线程结束。taskA所在线程在监听到退出请求后,处理事务然后调用 [NSThread exit]; 退出。

    -(void)createThread{//创建之后不会立即执行,需要start
        NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(taskA) object:nil];
        [thread1 start];
        
        
        //创建新的线程(方式2)--创建并立即执行
        [NSThread detachNewThreadSelector:@selector(taskB:) toTarget:self withObject:thread1];
    }


    -(void)taskA{ for (int i = 0 ; i<20; i++) { if ([[NSThread currentThread]isCancelled]) { //退出当前线程 [NSThread exit]; } NSLog(@"A = %d",i); [NSThread sleepForTimeInterval:0.25]; } } -(void)taskB:(NSThread *)thread{ for (int i = 0 ; i<20; i++) { if (i == 10) { [thread cancel]; } NSLog(@"B = %d",i); [NSThread sleepForTimeInterval:0.25]; } }

    3.线程的同步和锁

    线程的同步概念是指,多个线程对同一内存区域同时进行读写操作,这就回引发线程安全问题。当一个线程对数据的修改还没有完成,另一个线程进行访问的时候就会拿到理论上的旧的数据,从而引发问题。锁就是对这一问题的一个解决方案,给数据或方法加锁,每次只能有一个线程占有资源,就可以避免数据混乱的产生。

    @interface ViewController ()
    {
        int _num;
        NSLock * _lock;
    }
       _lock = [[NSLock alloc]init];//初始化
            [_lock lock];
            _num++;
            NSLog(@"add = %d",_num);
            [_lock unlock];    

    4.如何在线程中更新UI

    在iOS设计中,子线程是不允许更改UI的,UI的更新必须放在主线程中,这时可以在子线程中加入下面的代码,切换到主线程更新UI。

    waitUntilDone:参数是一个BOOL值,意思是在提供方法完成之前,当前线程是否等待。

    [self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:YES];

    5.NSOperation的使用

    5.1什么是NSOperation

      NSOperation 和nsthread相似,实现多线程的一种机制,在NSThread做了更高的抽象,加入block比NSThread简单易用. NSOperation 是一个抽象类,我们一般用它的子类:NSInvocationOperation,NSBlockOperation

    5.1NSInvocationOperation的使用

        //NSInvocationOperation
        NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(task1) object:nil];
        //注意:创建之后需要执行,执行的时候默认是同步的
        [invocationOperation start];

    task1为线程所要执行的方法。

    5.2NSBlockOperation的使用

        //NSBlockOperation
        NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0 ; i<20; i++) {
                NSLog(@"B = %d",i);
            }
        }];
        
        [blockOperation start];

    5.3NSOperationQueue的使用

        //操作队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //加入操作到队列,并行开始并行执行,这个操作讲一直存在在队列中知道被执行完毕
      //并行执行,这两个操作将同时执行
    [queue addOperation:invocationOperation]; [queue addOperation:blockOperation];

    6.GCD的使用

    6.1线程创建和方法使用

    创建两个异步线程

    - (void)createAsyncTask {
        //定义一个队列,这里为一个全局队列,第一个参数为队列的优先级,这里为默认优先级
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        //params1:传入queue,有三种queue
        //1 main queue UI所在
        //2 global queue 全局队列(工作线程)
        //3 自定义queue
        dispatch_async(queue, ^{
            for (int i = 0 ; i<20; i++) {
                NSLog(@"A = %d",i);
            }
        });
        dispatch_async(queue, ^{
            for (int i = 0 ; i<20; i++) {
                NSLog(@"B = %d",i);
            }
        });
    }

    整个程序仅执行一次的方法,可以线程安全的实现单例模式,下面的方法无论调用几次,都只将打印一次runOnce。

    -(void)runOnce{
        
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"runOnce");
        });
    }

    延时执行的方法,在“5”的位置可以填写延时的时间,单位为秒。

    -(void)delayRun{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"heh heh heh");
        });
    }

    将线程加入一个组,整个组的任务执行完毕后可以执行特定的操作

    -(void)groupRun{
    
        //group
        dispatch_group_t group = dispatch_group_create();
        
        //add task 7s done
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i<100; i++) {
                NSLog(@"a = %d",i);
                [NSThread sleepForTimeInterval:0.07];
            }
        });
        
        //add task 5s done
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i<100; i++) {
                NSLog(@"b = %d",i);
                [NSThread sleepForTimeInterval:0.05];
            }
        });
        
        //add task 6s done
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            for (int i = 0; i<100; i++) {
                NSLog(@"c = %d",i);
                [NSThread sleepForTimeInterval:0.06];
            }
        });
        
       
        dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"all tasks have done");
        });
    }

    6.2线程阻塞和执行顺序的讨论

    /* 在主队列开启同步任务,为什么会阻塞线程?
     > 在主队列开启同步任务,因为主队列是串行队列,里面的线程是有顺序的,先执行完一个线程才执行下一个线程,而主队列始终就只有一个主线程,主线程是不会执行完毕的,因为他是无限循环的,除非关闭应用程序。因此在主线程开启一个同步任务,同步任务会想抢占执行的资源,而主线程任务一直在执行某些操作,不肯放手。两个的优先级都很高,最终导致死锁,阻塞线程了。
     
     */
    - (void)main_queue_deadlock
    {
    
        dispatch_queue_t q = dispatch_get_main_queue();
        
        NSLog(@"1111");
        
        dispatch_async(q, ^{
            NSLog(@"主队列异步 %@", [NSThread currentThread]);
        });
        
        NSLog(@"2222");
        
        // 下面会造成线程死锁
        //    dispatch_sync(q, ^{
        //        NSLog(@"主队列同步 %@", [NSThread currentThread]);
        //    }); 
    
    }
    /**
     *  并行队列里开启同步任务是有执行顺序的,只有异步才没有顺序
     */
    - (void)concurrent_queue
    {
        dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_sync(q, ^{
            NSLog(@"同步任务 %@1111111", [NSThread currentThread]);
            dispatch_sync(q, ^{
                NSLog(@"同步任务 %@2222222", [NSThread currentThread]);
            });
            
            dispatch_sync(q, ^{
                NSLog(@"同步任务 %@333333", [NSThread currentThread]);
            });
    
        });
        
        dispatch_sync(q, ^{
            NSLog(@"同步任务 %@444444", [NSThread currentThread]);
        });
        
        dispatch_sync(q, ^{
            NSLog(@"同步任务 %@555555", [NSThread currentThread]);
        });
        dispatch_sync(q, ^{
            NSLog(@"同步任务 %@666666", [NSThread currentThread]);
        });
    }
    
    /**
     *  串行队列开启异步任务,是有顺序的
     */
    - (void)serial_queue
    {
        dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", DISPATCH_QUEUE_SERIAL);
    
        dispatch_async(q, ^{
            NSLog(@"异步任务 %@1111111", [NSThread currentThread]);
        });
        
        dispatch_async(q, ^{
            NSLog(@"异步任务 %@22222", [NSThread currentThread]);
        });
        
        dispatch_async(q, ^{
            NSLog(@"异步任务 %@3333", [NSThread currentThread]);
        });
        dispatch_async(q, ^{
            NSLog(@"异步任务 %@44444", [NSThread currentThread]);
        });
    
    
    
    }
    
    /**
     *  串行队列开启异步任务后嵌套同步任务造成死锁
     */
    - (void)serial_queue_deadlock2
    {
        dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", DISPATCH_QUEUE_SERIAL);
        
        
        dispatch_async(q, ^{
            NSLog(@"异步任务 %@", [NSThread currentThread]);
            // 下面开启同步造成死锁:因为串行队列中线程是有执行顺序的,需要等上面开启的异步任务执行完毕,才会执行下面开启的同步任务。而上面的异步任务还没执行完,要到下面的大括号才算执行完毕,而下面的同步任务已经在抢占资源了,就会发生死锁。
            dispatch_sync(q, ^{
                NSLog(@"同步任务 %@", [NSThread currentThread]);
            });
    
        });
    
    }
    
    /**
     *  串行队列开启同步任务后嵌套同步任务造成死锁
     */
    - (void)serial_queue_deadlock1
    {
        dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", DISPATCH_QUEUE_SERIAL);
        
        dispatch_sync(q, ^{
            NSLog(@"同步任务 %@", [NSThread currentThread]);
            // 下面开启同步造成死锁:因为串行队列中线程是有执行顺序的,需要等上面开启的同步任务执行完毕,才会执行下面开启的同步任务。而上面的同步任务还没执行完,要到下面的大括号才算执行完毕,而下面的同步任务已经在抢占资源了,就会发生死锁。
            dispatch_sync(q, ^{
                NSLog(@"同步任务 %@", [NSThread currentThread]);
            });
            
        });
        NSLog(@"同步任务 %@", [NSThread currentThread]);
    }
    
    
    /**
     *  串行队列开启同步任务后嵌套异步任务不造成死锁
     */
    - (void)serial_queue1
    {
        dispatch_queue_t q = dispatch_queue_create("cn.itcast.gcddemo", DISPATCH_QUEUE_SERIAL);
        
        dispatch_sync(q, ^{
            NSLog(@"同步任务 %@", [NSThread currentThread]);
            
            // 开启异步,就会开启一个新的线程,不会阻塞线程
            dispatch_async(q, ^{
                NSLog(@"异步任务 %@", [NSThread currentThread]);
            });
            
        });
        NSLog(@"同步任务 %@", [NSThread currentThread]);
    }
  • 相关阅读:
    My first blog in cnblog
    浅析JavaScript中this储存
    input 文本框密码框的只读属性
    Js 数组——filter()、map()、some()、every()、forEach()、lastIndexOf()、indexOf()
    jquery使用$.getJson()跨域大数据量请求方法
    JS中关于clientWidth offsetWidth scrollWidth 等的含义及区别
    JS性能优化
    npm 创建 node.js 项目
    css 垂直居中的几种方法
    字符串转数组
  • 原文地址:https://www.cnblogs.com/wantyoulee/p/4429609.html
Copyright © 2011-2022 走看看