zoukankan      html  css  js  c++  java
  • 多线程

    
    一、    进程、线程
    进程:正在进行中的程序被称为进程,负责程序运行的内存分配,每一个进程都有自己独立的虚拟内存空间
    线程:线程是进程中一个独立的执行路径(控制单元),一个进程中至少包含一条线程,即主线程(UI操作),可以将耗时的执行路径(如网络请求)放在其他线程中执行,线程不能被杀掉,但是可以暂停/休眠一条线程。
    创建线程的目的:开启一条新的执行路径,运行指定的代码,与主线程中的代码实现同时运行
    多线程的优势:充分发挥多核处理器优势,将不同线程任务分配给不同的处理器,真正进入"并行运算"状态;将耗时的任务分配到其他线程执行,由主线程负责统一更新界面会使应用程序更加流畅,用户体验更好;当硬件处理器的数量增加,程序会运行更快,而程序无需做任何调整.。
    弊端:新建线程会消耗内存空间和CPU时间,线程太多会降低系统的运行性能
    二、    NSThread
    优点:NSThread 比其他两个轻量级
    缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
    使用:
    1、    NSThread 有两种直接创建方式:
    -(id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;
    +(void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(test:) object:nil];// selector最多只能有一个参数,且不能有返回值,object,传输的参数,可以为nil
    thread.name = @"线程1";//设置线程名
    //第一种创建方式需要我们手动开启线程
    [thread start];//开启线程
    [NSThread detachNewThreadSelector:@selector(test:) toTarget:self withObject:@"345"];//调用完毕后马上创建并开始线程
    2、    隐式创建
    [self performSelectorInBackground:@selector(test:) withObject:@"456"];
    3  线程间通讯
    回到主线程执行testMain:方法    [self performSelectorOnMainThread:@selector(testMain:) withObject:nil waitUntilDone:YES];// waitUntilDone是指是否等到testMain:方法执行完了,才向下执行。
    在指定线程上执行操作:[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];
    在本线程执行:    [self performSelector:@selector(run) withObject:nil];
    4、    [NSThread sleepForTimeInterval:2];//当前线程暂停2秒
    5、    NSDate *date = [NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]]; 
    [NSThread sleepUntilDate:date];//以当前时间为标准,延迟到某个时间再执行该线程操作
    6、    获取主线程:    NSThread *main = [NSThread mainThread];
    7、    获取当前线程:    NSThread *current = [NSThread currentThread];
    8、    结束线程:[NSThread exit];//当前线程方法中使用
         [thread cancel];//主线程中使用
    三、    Cocoa NSOperation
    优点:不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。
    NSOperation实例封装了需要执行的操作和执行操作所需的数据,并且能够以并发或非并发的方式执行这个操作。
    NSOperation本身是抽象基类,因此必须使用它的子类,使用NSOperation子类的方式有2种:一种是用定义好的两个子类:NSInvocationOperation 和 NSBlockOperation,另一种是自定义子类继承NSOperation,实现内部相应的方法
    1、    NSOperation的作用:配合使用NSOperation和NSOperationQueue也能实现多线程编程取消操作
    2、    NSOperation和NSOperationQueue实现多线程的具体步骤:
    先将需要执行的操作封装到一个NSOperation对象中
    然后将NSOperation对象添加到NSOperationQueue中
    系统会⾃动将NSOperationQueue中的NSOperation取出来
    将取出的NSOperation封装的操作放到⼀条新线程中执⾏
    3、    NSInvocationOperation
    //创建操作对象,封装要执行的任务
    //NSInvocationOperation   封装操作
    NSInvocationOperation *operation=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test) object:nil]; 
    //执行操作
    [operation start];
    注意:操作对象默认在主线程中执行,只有添加到队列中才会开启新的线程。即默认情况下,如果操作没有放到队列queue中,都是同步执行。只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作 
    4、    NSBlockOperation
    NSBlockOperation *operation1=[NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"NSBlockOperation------%@",[NSThread currentThread]);
      }];
      //开启执行操作
    [operation1 start];
    
    5、    添加操作(注意:NSInvocationOperation无添加操作的功能)
    //添加操作
    [operation1 addExecutionBlock:^{
    NSLog(@"NSBlockOperation1---++-%@",[NSThread currentThread]);
    }];//此操作放在start前
    注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作 
    6、    NSOperationQueue
    NSOperationQueue的作⽤:NSOperation可以调⽤start⽅法来执⾏任务,但默认是同步执行的,使用NSOperationQueue时,无需start
    
    如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
    添加操作到NSOperationQueue中,自动执行操作,自动开启线程
    7//创建NSOperationQueue 
    NSOperationQueue * queue=[[NSOperationQueue alloc]init];
    8、    把操作添加到队列中
    第一种:[queue addOperation:operation1];//添加之前创建的
    第二种:[queue addOperationWithBlock:^{
         NSLog(@"NSBlockOperation3--4----%@",[NSThread currentThread]);
    }];//添加的时候创建
    注意:使用NSOperationQueue时,无需start
    四、    GCD全称:Grand Central Dispatch
        Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。在iOS4.0开始之后才能使用。GCD是一个替代诸如NSThread, NSOperationQueue, NSInvocationOperation等技术的很高效和强大的技术。
        GCD的工作原理是:让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务
        GCD是基于C语言的。如果使用GCD,完全由系统管理线程,我们不需要编写线程代码。只需定义想要执行的任务,然后添加到适当的调度队列(dispatch queue)。GCD会负责创建线程和调度你的任务,系统直接提供线程管理
    1、    常用的方法dispatch_async
    为了避免界面在处理耗时的操作时卡死,比如读取网络数据,IO,数据库读写等,我们会在另外一个线程中处理这些操作,然后通知主线程更新界面。
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // 耗时的操作
            dispatch_async(dispatch_get_main_queue(), ^{
                // 更新界面
            });
    });
    dispatch_async开启一个异步操作,第一个参数是指定一个gcd队列,第二个参数是分配一个处理事物的程序块到该队列。
    dispatch_get_global_queue(0, 0),指用了全局队列。
    一般来说系统本身会有3个队列:global_queue、current_queue(废弃)和main_queue
    获取一个全局队列是接受两个参数,第一个是我分配的事物处理程序块队列优先级。分高低和默认,0为默认2为高,-2为低
    2、    dispatch_group_async的使用
    dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成了。
     // 创建一个组
        dispatch_group_t group = dispatch_group_create();
        //在一个组内提交一个代码块来执行。必须明确这个代码块属于哪个组,必须在哪个派送队列上执行。
        dispatch_group_async(group, queue, ^{
           NSLog(@"group1--%@",[NSThread currentThread]);
        });
        dispatch_group_async(group, queue, ^{
            NSLog(@"group2--%@",[NSThread currentThread]);    });
        dispatch_group_async(group, queue, ^{
            NSLog(@"group3--%@",[NSThread currentThread]);    });
        // 等待组中的任务执行完毕,回到主线程执行block回调
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"updateUi");
        });   
    3、    dispatch_barrier_async barrier:障碍
    dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
         //创建队列,第一个参数代表队列名,第二个代表串行还是并行:DISPATCH_QUEUE_SERIAL串行   DISPATCH_QUEUE_CONCURRENT 并行
      dispatch_queue_t queue = dispatch_queue_create("123", DISPATCH_QUEUE_SERIAL(串行队列));//#define DISPATCH_QUEUE_CONCURRENT(并发执行队列)
       //中央调度
        dispatch_async(queue, ^{
            NSLog(@"dispatch_async1");
    });
    //上一步执行结束才执行这一步
        dispatch_barrier_async(queue, ^{
            NSLog(@"dispatch_barrier_async");
    });
    //执行完上一步才执行下一步,中央调度
        dispatch_async(queue, ^{
            NSLog(@"dispatch_async3");
        });
    4、    dispatch_apply 
         它一般都是放在dispatch_async里面(异步)。
    执行某个代码片段N次
      dispatch_async(dispatch_get_global_queue(0, 0), ^{
            dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t index) {
                NSLog(@"%ld",index);
            });  
        });
     1 #import "ViewController.h"
     2 
     3 @interface ViewController ()
     4 
     5 @end
     6 
     7 @implementation ViewController
     8 
     9 - (void)viewDidLoad {
    10     [super viewDidLoad];
    11 
    12     UIImageView *imageView = [[UIImageView alloc] initWithFrame: CGRectMake(30, 40, 100, 100)];
    13     [self.view addSubview:imageView];
    14     
    15     UIImageView *imageView2 = [[UIImageView alloc] initWithFrame: CGRectMake(30, 150, 100, 100)];
    16     [self.view addSubview:imageView2];
    17     
    18     UIImageView *imageView3 = [[UIImageView alloc] initWithFrame: CGRectMake(30, 260, 100, 100)];
    19     [self.view addSubview:imageView3];
    20     
    21     UIImageView *imageView4 = [[UIImageView alloc] initWithFrame: CGRectMake(30, 370, 100, 100)];
    22     [self.view addSubview:imageView4];
    23 /*
    24      为了避免界面在处理耗时的操作时卡死,比如读取网络数据,IO,
    25      数据库读写等,我们会在另外一个线程中处理这些操作,然后通知
    26      主线程更新界面。
    27 */
    28     dispatch_async(dispatch_get_global_queue(0, 0), ^{
    29       //  NSLog(@"耗时操作%@",[NSThread currentThread]);
    30         
    31         NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://www.yooyoo360.com/photo/2009-1-1/20090112132752467.jpg"]];
    32         
    33         UIImage *image = [UIImage imageWithData:data];
    34       //  NSLog(@"--%@",image);
    35         //返回主线程
    36         dispatch_async(dispatch_get_main_queue(), ^{
    37           //  NSLog(@"更新UI%@",[NSThread currentThread]);
    38             imageView.image = image;
    39 
    40         });
    41     });
    42 /*
    43  dispatch_async开启一个异步操作,第一个参数是指定一个gcd队列,
    44  第二个参数是分配一个处理事物的程序块到该队列。
    45  dispatch_get_global_queue(0, 0),指用了全局队列。
    46  一般来说系统本身会有3个队列:global_queue、current_queue(废弃)和main_queue
    47  获取一个全局队列是接受两个参数,第一个是我分配的事物处理程序块队列优先级。
    48  分高低和默认,0为默认2为高,-2为低
    49  */
    50 /*
    51  dispatch_group_async可以实现监听一组任务是否完成,
    52  完成后得到通知执行其他的操作。这个方法很有用,比如你执行三
    53  个下载任务,当三个任务都下载完成后你才通知界面说完成了。
    54 */
    55     //56     //创建组:用于存放耗时操作
    57     dispatch_group_t group = dispatch_group_create();
    58     
    59     __block UIImage *image1,*image2,*image3;
    60     
    61     //在一个组内提交一个代码块来执行。必须明确这个代码块属于哪个组,必须在哪个派送队列上执行。
    62     //将操作封装进组,第一个参数代表要存放操作的组名,第二个参数代表操作队列,block执行耗时操作
    63     dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
    64         NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://pic2.ooopic.com/10/81/58/62bOOOPICce.jpg"]];
    65         image1 = [UIImage imageWithData:data];
    66        // NSLog(@"--1%@",image);
    67         
    68     });
    69     
    70     dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
    71          NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://www.downsc.com/vector_pic/shiliang_iecool/5/3/b_img/14430.jpg"]];
    72         image2 = [UIImage imageWithData:data];
    73         //NSLog(@"--2%@",image);
    74     });
    75     
    76     dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
    77         NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://www.downsc.com/vector_pic/shiliang_iecool/5/2/b_img/13788.jpg"]];
    78        image3 = [UIImage imageWithData:data];
    79       //  NSLog(@"--3%@",image);
    80     });
    81     
    82      // 等待组中的任务执行完毕,回到主线程执行block回调
    83     //监听一组操作,第一个参数代表要监听的组名,第二个参数代表一组操作完全结束后跳转到哪个队列,一般跳到主线程(dispatch_get_main_queue),block执行要进行的操作(一般用来更新UI),注意:此方法在组中的所有操作执行完毕后调用
    84     dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    85         imageView2.image = image1;
    86         imageView3.image = image2;
    87         imageView4.image = image3;
    88     });
    89 }
    90 
    91 - (void)didReceiveMemoryWarning {
    92     [super didReceiveMemoryWarning];
    93     // Dispose of any resources that can be recreated.
    94 }
    95 
    96 @end

    dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行

         //创建队列,第一个参数代表队列名,第二个代表串行还是并行:DISPATCH_QUEUE_SERIAL串行   DISPATCH_QUEUE_CONCURRENT 并行

      dispatch_queue_t queue = dispatch_queue_create("123", DISPATCH_QUEUE_SERIAL);

        dispatch_async(queue, ^{

            NSLog(@"dispatch_async1");

    });

    //上一步执行结束才执行这一步

        dispatch_barrier_async(queue, ^{

            NSLog(@"dispatch_barrier_async");

    });

    //执行完上一步才执行下一步

        dispatch_async(queue, ^{

            NSLog(@"dispatch_async3");

        });

    1、       dispatch_apply 

         它一般都是放在dispatch_async里面(异步)。

    执行某个代码片段N次

      dispatch_async(dispatch_get_global_queue(0, 0), ^{

            dispatch_apply(5, dispatch_get_global_queue(0, 0), ^(size_t index) {

                NSLog(@"%ld",index);

            }); 

        });

  • 相关阅读:
    Uva 11991 Easy Prblem from Rujia Liu ?
    BANK && IT
    随机数发生器(对拍)-----对比测试
    HDU 1695(GCD)
    欧拉定理与费马定理,离散对数定理
    POJ 2352 (stars)
    线段树
    codeforces 51C(Three Base Stations)
    codeforces 165B(Burning Midnight Oil)
    POJ 2785(4 Values whose Sum is 0)
  • 原文地址:https://www.cnblogs.com/liuguan/p/5032758.html
Copyright © 2011-2022 走看看