zoukankan      html  css  js  c++  java
  • iOS多线程技术---pthread、NSThread、NSOperation、GCD

    多线程技术

    process进程:在系统中运行的一个应用程序;每个进程都是独立的;有专有的内存空间

    thread线程:程序的一段执行序列,进程的不部分;

                 特点:1、进程只分配内存空间,并不执行任务

                             2、每个进程至少有一个线程,该线程叫做主线程

                             3、线程是进程的基本执行单元,进程的所有任务都是在线程中执行

                             4、每个线程中得任务的执行都是串行的

     

    多线程并发:一个进程中多条线程并发执行任务;

                特点:   1、提高程序的执行效率,提高资源利用率

                            2、同一时间,CPU只能处理一条线程

                            3、多线程并发时,CPU在多条线程间快速切换

                            4、线程切换的 速度很快,就造成了多线程的并发执行

                            5、开启线程需要内存空间,线程太多会造成调度消耗太多资源

                            6、线程过多会降低每条线程被调度的频率(线程执行效率降低)

     

    多线程的应用:

            主线程:显示刷新UI界面、处理UI事件;耗时任务(如下载)放在子线程中

            判定方法在哪个线程中执行:NSLog(@“当前的线程:”,[NSThread currentThread]);

     

    四种多线程技术:pthread   NSThread   GCD  NSOperation

     

    1.pthread

          基于c语言的API ,可编写多平台应用,使用难度大,需要手动创建销毁线程,可自定义功能少

    pthread_t pthread;

    void *task(void *data){

    NSLog(@“当前子线程:%@“,NSThread currentThread];

    return 0;

    }

     

    pthread_create(&pthread,NULL,task,NULL);

     

    2.NSThread

    • 基于OC,相对于pthread使用简单,面向对象
    • 缺点:需要自己管理线程的生命周期,线程同步,加锁、开锁,管理多个线程比较困难

     

    手动创建线程的三种方法:

    1、NSThread实例方法:- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument NS_AVAILABLE(10_5, 2_0);(只创建

    2、NSThread类方法:+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument; (创建并运行

    3、NSThread实例方法:- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg NS_AVAILABLE(10_5, 2_0);

    注:selector: 线程执行的方法,这个selector只能由一个参数,而且没有返回值

            object: 传给target的唯一参数,也可以为nil

            target: selector消息发送的对象 只有第一个能设置名字、优先级

    线程阻塞(延迟):

    • // sleep⽅法⼀:开始睡眠5秒钟

    [NSThread sleepForTimeInterval:5];

    • // sleep⽅法二:

    NSDate *date = [NSDate dateWithTimeIntervalSinceNow:3

    [NSThread sleepUntilDate:date];

    强制停止线程:

    [NSThread exit];

    其他常用方法:

    • +(NSThread *)mainThread; //获得主线程
    • -(BOOL)isMainThread;//判断是否是主线程
    • +(double)threadPriority;//获取线程优先级    优先级取值范围0.0~1.0 值越大优先级越高
    • +(BOOL)setThreadPriority:(double)p;//设置优先级
    • -(NSString*)name;//获取线程名字
    • -(void)setName:(NSString*)n;//设置线程名字

     

    线程的状态:

    • New(新建) -> Runnable(就绪) --> cpu开始调度当前线程 ---> Running(运行) ---> 调用 sleep方法 ---> Blocked(阻塞) ---> 移除线程池 ---> sleep执行完毕/到时 ---> 进入线程 池 ---> Runnable(就绪)
    • 如果线程任务执行完毕/异常/强制退出 ---> Dead(死亡)

    加锁:

    • 尽量避免使用@synchronized,也就是避免多个线程访问同一个资源,因为有了加锁、 解锁需要消耗比较大的cpu资源 
    • 加锁的前提:多个线程同时访问同一个资源的时候才需要加锁(互斥锁) 
    • 线程同步:多条线程在同一条线上执行(按顺序地执行任务) 
    • 尽量讲加锁、资源抢夺的业务逻辑交割服务器端处理,减少移动客户端的压力

    synchronized关键字

    1、synchronized关键字的作用域有二种:

    1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;

    2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用

    2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){},它的作用域是当前对象;

    原子和非原子属性:

    • nonatomic: 非原子 —> 不会在set方法中加锁,这个是推荐方式,会占用资源少
    • atomic:原子 —> 在set方法中加锁,防止多个线程同时执行set方法

     

    线程间的通讯

    1.在一个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 

    2.线程间通讯的方式:

    • 一个线程传递数据给另一个线程
    • 在一个线程中执行完特定任务后,转到另一个线程继续执行任务

    子线程返回主线程的两种方法:

     - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait

    ***********************************************************************************************

    3. GCD

     并行队列只有在执行多个异步任务的时候体现出来并发现象,因为在线程快速切换时体现出并行;

     同步执行的任务 会致使后面的任务提交阻塞;

    GCD:Grand Central Dispath 大中央调度

     
    • GCD的基本思想就是将操作(任务)放在队列中去执行
    • 队列负责调度任务执行所在的线程以及具体的执行时间
    • 队列的特点是先进先出,新添加至队列的操作(任务)都会排在队尾
     
    GCD的函数都是以dispatch开头的,dispatch的意思是“分配、调度”
     
    • 串行队列中的任务会按顺序执行
    • 并行队列中的任务通常会并发执行,而且无法确定任务的执行顺序
     
    dispatch_async表示异步操作,异步操作会新开辟线程来执行任务,而且无法确定任务的执行顺序
    dispatch_sync表示同步操作,同步操作不会新开辟线程
     
    1. 在串行队列中执行同步任务:不会新建线程,按顺序执行任务(毫无用处)
    2. 在串行队列中执行异步任务,会新建线程,按顺序执行任务(非常有用)
     
    1. 在并行队列中执行同步任务:不会新建线程,按顺序执行任务(几乎没用)
    2. 在并行队列中执行异步任务:会新建多个线程,但是无法确定任务的执行顺序(有用,但是很容易出错)
     
    全局队列
    • 全局队列是系统的,直接拿过来就可以用,与并行队列类似,但是不能指定队列的名字,调试时无法确认任务所在队列
    • 在全局队列中执行同步任务:不会新建线程,按顺序执行任务
    • 在全局队列中执行异步任务:会新建多个线程,但是无法确定任务的执行顺序
    主队列
    • 如果把任务放到主队列中进行处理,那么不论处理函数是异步的还是同步的都不会开启新的线程。
    • 每一个应用程序只有一个主线程即只有一个主队列
    • 为什么需要再主线程上执行任务呢?因为在ios开发中,所有UI的更新任务都必须在主线程上执行。
    • 主队列中的操作都是在主线程中执行的,不存在异步的概念
    • 在主线程中向 主队列中添加的同步操作会死锁

    线程阻塞(延迟):三种方法

     1 //过3秒后做一件事儿,这个做法会导致主线程阻塞,不提倡使用
     2 - (IBAction)sleepDelay:(id)sender
     3 {
     4     NSLog(@"3秒后干件事儿!");
     5     [NSThread sleepForTimeInterval:3];
     6     NSLog(@"干事儿中...");
     7 }
     8 - (IBAction)performAfterDelay:(id)sender
     9 {
    10     NSLog(@"3秒后干件事儿!");
    11     //在3秒后会启动一个线程去完成任务(调用方法task),此方法并不会阻塞
    12     [self performSelector:@selector(task) withObject:nil afterDelay:3];
    13     NSLog(@"主线程继续向下执行...");
    14 }
    15 - (void)task
    16 {
    17     NSLog(@"干活中...");
    18 }
    19 //使用GCD实现延迟执行
    20 - (IBAction)dispatchAfter:(id)sender
    21 {
    22     /*
    23      1秒 = 1000毫秒
    24      1毫秒 = 1000微秒
    25      1微秒 = 1000纳秒
    26      */
    27     //dispatch_time函数的第二个参数单位是纳秒
    28     NSLog(@"3秒钟后做事");
    29     dispatch_after(
    30                    /**
    31                     *  延迟的函数
    32                     *
    33                     *  @param DISPATCH_TIME_NOW 从现在起
    34                     *  @param int64_t           延迟描述
    35                     *    NSEC_PER_SEC 这个宏的意思是每秒多少纳秒
    36                     *  @return 无
    37                     */
    38         dispatch_time(DISPATCH_TIME_NOW, (int64_t)3 * NSEC_PER_SEC),
    39         dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
    40         ^{
    41             NSLog(@"3秒后我开始做需要的事情啦!");
    42     });
    43 }
    View Code

    线程间的通讯:

    dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执⾏耗时的异步操作...
    dispatch_async(dispatch_get_main_queue(), ^{
    
    // 回到主线程,执⾏UI刷新操作
    });
    });  

    dispatch_once  参照:线程安全的单例  http://www.cnblogs.com/ChrisYu/p/4651114.html

    小结:

           无论什么队列和什么任务,线程的创建和回收不需要程序员参与,由队列来负责,程序员只需要面对队列和任务。GCD在后端管理这一个线程池,GCD不仅决定着Block代码块将在哪个线程中被执行,而且还可以根据可用的系统资源对这些线程进行管理,从而让开发者从线程管理的工作中解放出来,通过GCD这种集中的管理线程,缓解了大量的线程被创建的问题

     

     

    4. NSOperation

     

     

     

    单词:

    1. global   ge(0) lao(1) bao(3)  全局

    2. concurrent   并发

    3. queue  队列

     

     

    作业:

    1. 从网上下载图片并显示到界面上。

    要求,下载过程中界面不能死。

    用GCD做

     

    2. 了解一个第三方框架SDWebImage

     1 - (IBAction)serialOperation
     2 {
     3     //默认OperationQueue中的线程都是并行的
     4     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
     5     NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printPlusSignal) object:nil];
     6 //    [operation1 start];//不会启动线程,直接在主线程中执行
     7     [queue addOperation:operation1];
     8     NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printMinusSignal) object:nil];
     9     //[operation2 start];
    10     //线程二要依赖线程一,所以线程2会等线程一结束了再执行,按此方式可以让两个线程串行执行
    11     [operation2 addDependency:operation1];
    12     [queue addOperation:operation2];
    13 }
    14 
    15 - (void)printPlusSignal
    16 {
    17     for (int i=0; i<10; i++) {
    18         [NSThread sleepForTimeInterval:1];
    19         NSLog(@"++++++++++++++");
    20     }
    21 }
    22 
    23 - (void)printMinusSignal
    24 {
    25     for (int i=0; i<10; i++) {
    26         [NSThread sleepForTimeInterval:1];
    27         NSLog(@"----------------");
    28     }
    29 }
    30 
    31 - (IBAction)concurrentQueue:(id)sender
    32 {
    33     //默认OperationQueue中的线程都是并行的
    34     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    35     NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printPlusSignal) object:nil];
    36    
    37     [queue addOperation:operation1];
    38     NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printMinusSignal) object:nil];
    39 
    40     [queue addOperation:operation2];
    41 }
    42 
    43 //使用Block提交任务给OperationQueue
    44 - (IBAction)blockQueue
    45 {
    46     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    47     //CallBack 回调
    48     NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    49         for (int i=0; i<10; i++) {
    50             [NSThread sleepForTimeInterval:1];
    51             NSLog(@"++++++++++++++");
    52         }
    53     }];
    54     [queue addOperation:operation1];
    55     
    56     NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    57         for (int i=0; i<10; i++) {
    58             [NSThread sleepForTimeInterval:1];
    59             NSLog(@"-------------");
    60         }
    61     }];
    62     [queue addOperation:operation2];
    63 }
    64 - (IBAction)mainQueue
    65 {
    66     NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    67     NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    68         NSLog(@"在子线程中-----%@", [NSThread currentThread]);
    69         //获取主队列(回到主线程)
    70         NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    71         NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    72             NSLog(@"在主线程中?-----%@", [NSThread currentThread]);
    73         }];
    74         [mainQueue addOperation:operation2];
    75     }];
    76     [queue addOperation:operation];
    77 }
    View Code
  • 相关阅读:
    解读《TCP/IP详解》(卷1):05章:RARP(逆地址解析协议)
    Android中利用“反射”动态加载R文件中的资源
    “骆驼”怎么写
    eclipse无法访问genymotion模拟器下/data/data文件解决方案
    解读《TCP/IP详解》(卷1):03章:IP(网际协议)
    解读《TCP/IP详解》(卷1):02章:链路层
    hdu1039 java正则表达式解法
    hdu1027 又是next_permutaiton
    hdu1261 java水过高精度排列组合。。
    hdu1716 STL next_permutation函数的使用
  • 原文地址:https://www.cnblogs.com/ChrisYu/p/4649968.html
Copyright © 2011-2022 走看看