美文网首页
多线程总结

多线程总结

作者: freemanIT | 来源:发表于2017-11-22 14:30 被阅读0次

    概念

    • 进程: 1.一个具有一定独立功能的程序关于某个数据集合的一次运行活动。可以理解成一个运行中的应用程序。2.每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内

    • 线程: 1. 1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)。2. 一个进程(程序)的所有任务都在线程中执行

    • 线程的串行: 如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务,也就是说,在同一时间内,1个线程只能执行1个任务

    进程和线程的比较

    1.线程是CPU调用(执行任务)的最小单位。
    2.进程是CPU分配资源和调度的单位。
    3.一个程序可以对应多个进程,一个进程中可以有多个线程,但至少要有一个线程。
    4.同一个进程内的线程共享进程的资源。

    • 多线程: 1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务, 多线程技术可以提高程序的执行效率
    多线程原理:
    1.同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)
    2.多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)
    3.如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
    
    如果线程非常非常多:
    1.CPU会在N多线程之间调度,消耗大量的CPU资源
    2.每条线程被调度执行的频次会降低(线程的执行效率降低)
    

    多线程的优缺点

    • 多线程的优点
      1.能适当提高程序的执行效率
      2.能适当提高资源利用率(CPU、内存利用率)
    • 多线程的缺点
      1.创建线程是有开销的,iOS下主要成本包括:内核数据结构(大约1KB)、栈空间(子线程512KB、主线程1MB,也可以使用-setStackSize:设置,但必须是4K的倍数,而且最小是16K),创建线程大约需要90毫秒的创建时间
      2.如果开启大量的线程,会降低程序的性能
      3.线程越多,CPU在调度线程上的开销就越大
      4.程序设计更加复杂:比如线程之间的通信、多线程的数据共享

    多线程在iOS开发中的应用

    • 一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”
    • 主线程的主要作用: 显示\刷新UI界面, 处理UI事件(比如点击事件、滚动事件、拖拽事件等)
    • 主线程的使用注意: 别将比较耗时的操作放到主线程中, 耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验, 将耗时操作放在子线程执行
    //获得主线程
    NSThread *mainThread = [NSThread mainThread];
    //获得当前线程
    NSThread *currentThread  = [NSThread currentThread];
    //判断主线程
    BOOL isMainThread = [NSThread isMainThread];
    

    iOS多线程实现方案

    多线程.jpg
    • pthread
    首先导入头文件
    #import <pthread.h>
    
        //1.创建线程对象
        pthread_t thread;
        
        //2.创建线程
        /*
         第一个参数:线程对象 传递地址
         第二个参数:线程的属性 NULL
         第三个参数:指向函数的指针
         第四个参数:函数需要接受的参数
         */
        pthread_create(&thread, NULL, task, NULL);
    
    void *task(void *param)
    {
    //    NSLog(@"%@--------",[NSThread currentThread]);
        return NULL;
    }
    
    • NSThread
    方法一:
    //1.alloc init 创建线程,需要手动启动线程
    //线程的生命周期:当任务执行完毕之后被释放掉
    
       //1.创建线程
        /*
         第一个参数:目标对象  self
         第二个参数:方法选择器 调用的方法
         第三个参数:前面调用方法需要传递的参数 nil
         */
        NSThread *threadA = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"ABC"];
        
        //设置属性
        threadA.name = @"线程A";
        //设置优先级  取值范围 0.0 ~ 1.0 之间 最高是1.0 默认优先级是0.5
        threadA.threadPriority = 1.0;
        
        //2.启动线程
        [threadA start];
    
    方法二 : 
    //2.分离子线程,自动启动线程
    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"分离子线程"];
    
    方法三: 
    //3.开启一条后台线程
    [self performSelectorInBackground:@selector(run:) withObject:@"开启后台线程"];
    
    -(void)run:(NSString *)param
    {
    //    NSLog(@"---run----%@---%@",[NSThread currentThread].name,param);
        for (NSInteger i = 0; i<10000; i++) {
            NSLog(@"%zd----%@",i,[NSThread currentThread].name);
        }
    }
    
    

    线程状态:

    //阻塞线程
        //[NSThread sleepForTimeInterval:2.0];
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
    
    // [NSThread exit];  //退出当前线程
    
    

    多线程的安全隐患:

    资源共享
    1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
    比如多个线程访问同一个对象、同一个变量、同一个文件
    当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题

    
    self.totalCount = 100;
        
         self.threadA = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
         self.threadB = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
         self.threadC = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
        
        self.threadA.name = @"售票员A";
        self.threadB.name = @"售票员B";
        self.threadC.name = @"售票员C";
        
        //启动线程
        [self.threadA start];
        [self.threadB start];
        [self.threadC start];
      
    
    -(void)saleTicket
    {
        while (1) {
            
        //锁:必须是全局唯一的
        //1.注意枷锁的位置
        //2.注意枷锁的前提条件,多线程共享同一块资源
        //3.注意加锁是需要代价的,需要耗费性能的
        //4.加锁的结果:线程同步
            
        @synchronized(self) {
            //线程1
            //线程2
            //线程3
            NSInteger count = self.totalCount;
            if (count >0) {
                
                for (NSInteger i = 0; i<1000000; i++) {
                }
                
                self.totalCount = count - 1;
                //卖出去一张票
                NSLog(@"%@卖出去了一张票,还剩下%zd张票", [NSThread currentThread].name,self.totalCount);
            }else
            {
                NSLog(@"不要回公司上班了");
                break;
            }
            }
        }
        
    }
    
    • GCD:

    纯C语言的函数, 提供了非常强大的函数
    优势:

    1. GCD是苹果公司为多核的并行运算提供的解决方案
    2. GCD会自动的利用更多的CPU 内核
    3. GCD会自动的管理线程的生命周期
    4. 程序员只需要告诉GCD 想要执行什么任务, 并不需要编写线程管理代码
    • 任务: 执行什么操作, 遵循 FIFO 原则
    • 队列: 用来存放任务
      • 并发队列: 可以让多个任务并发执行(自动开启线程同时执行任务), 但是只有在异步函数下才有效

      • 串行队列: 让任务一个个的执行(一个任务执行完毕之后, 再执行下一个任务)

      • 同步: 只能在当前线程中执行任务, 不开启新的线程

      • 异步: 可以在新的线程中执行任务, 具备开启新线程的能力

    GCD 的基本使用

    // 1. 异步函数+并发队列:会开启多条线程,队列中的任务是并发执行
    -(void)asyncConcurrent
    {
        //1.创建队列
        /*
         第一个参数:C语言的字符串,标签
         第二个参数:队列的类型
            DISPATCH_QUEUE_CONCURRENT:并发
            DISPATCH_QUEUE_SERIAL:串行
         */
        //dispatch_queue_t queue = dispatch_queue_create("com.sina", DISPATCH_QUEUE_CONCURRENT);
        
        //获得全局并发队列
        /*
         第一个参数:优先级
         第二个参数:
         */
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        NSLog(@"---satrt----");
        
        //2. 封装任务
        /*
         第一个参数:队列
         第二个参数:要执行的任务
         */
        dispatch_async(queue, ^{
            NSLog(@"download1----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"download2----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"download3----%@",[NSThread currentThread]);
        });
        
         NSLog(@"---end----");
    }
    
    
    // 2. 异步函数+串行队列:会开线程,开一条线程,队列中的任务是串行执行的
    -(void)asyncSerial
    {
        //1.创建队列
        dispatch_queue_t queue = dispatch_queue_create("com.sina", DISPATCH_QUEUE_SERIAL);
    
        //2.封装操作
        dispatch_async(queue, ^{
            NSLog(@"download1----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"download2----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"download3----%@",[NSThread currentThread]);
        });
    }
    
    // 3. 同步函数+并发队列:不会开线程,任务是串行执行的
    -(void)syncConcurrent
    {
        //1.创建队列
        dispatch_queue_t queue = dispatch_queue_create("com.sina", DISPATCH_QUEUE_CONCURRENT);
        
        NSLog(@"---start---");
        //2.封装任务
        dispatch_sync(queue, ^{
            NSLog(@"download1----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"download2----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"download3----%@",[NSThread currentThread]);
        });
        
         NSLog(@"---end---");
    }
    
    // 4. 同步函数+串行队列:不会开线程,任务是串行执行的
    -(void)syncSerial
    {
        //1.创建队列
        dispatch_queue_t queue = dispatch_queue_create("com.sina", DISPATCH_QUEUE_SERIAL);
        
        //2.封装任务
        dispatch_sync(queue, ^{
            NSLog(@"download1----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"download2----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"download3----%@",[NSThread currentThread]);
        });
    }
    主队列相关:
    // 1. 异步函数+主队列:所有任务都在主线程中执行,不会开线程
    -(void)asyncMain
    {
        //1.获得主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
    
        //2.异步函数
        dispatch_async(queue, ^{
            NSLog(@"download1----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"download2----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"download3----%@",[NSThread currentThread]);
        });
    }
    
    // 2. 同步函数+主队列:死锁
    //注意:如果该方法在子线程中执行,那么所有的任务在主线程中执行,
    -(void)syncMain
    {
        //1.获得主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        NSLog(@"start----");
        //2.同步函数
        //同步函数:立刻马上执行,如果我没有执行完毕,那么后面的也别想执行
        //异步函数:如果我没有执行完毕,那么后面的也可以执行
        dispatch_sync(queue, ^{
            NSLog(@"download1----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"download2----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"download3----%@",[NSThread currentThread]);
        });
        
        NSLog(@"end---");
    }
    
    
    同步异步.jpg

    GCD 线程之间的通信:

    //1.创建子线程下载图片
        //DISPATCH_QUEUE_PRIORITY_DEFAULT 0
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
           
            //1.1 确定url
            NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1511338633697&di=294a5b722555e26ba1c5c08b60b4a8b7&imgtype=0&src=http%3A%2F%2Fimg.sucaifengbao.com%2Fvector%2Flogosjbz%2F31_376_bp.jpg"];
            
            //1.2 下载二进制数据到本地
           NSData *imageData =  [NSData dataWithContentsOfURL:url];
            
            //1.3 转换图片
            UIImage *image = [UIImage imageWithData:imageData];
            
            NSLog(@"download----%@",[NSThread currentThread]);
            
            //更新UI
    //        dispatch_async(dispatch_get_main_queue(), ^{
            dispatch_sync(dispatch_get_main_queue(), ^{
                self.imageView.image = image;
                 NSLog(@"UI----%@",[NSThread currentThread]);
            });
            
        });
    

    GCD 延迟执行

    //延迟执行
    -(void)delay
    {
        NSLog(@"start-----");
        
        //1. 延迟执行的第一种方法
        //[self performSelector:@selector(task) withObject:nil afterDelay:2.0];
        
        //2.延迟执行的第二种方法
        //[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
        
        //3.GCD
    //    dispatch_queue_t queue = dispatch_get_main_queue();
         dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        /*
         第一个参数:DISPATCH_TIME_NOW 从现在开始计算时间
         第二个参数:延迟的时间 2.0 GCD时间单位:纳秒
         第三个参数:队列
         */
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
            NSLog(@"GCD----%@",[NSThread currentThread]);
        });
    
    }
    

    GCD 执行一次性代码

    //一次性代码
    //不能放在懒加载中的,应用场景:单例模式
    -(void)once
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"---once----");
        });
    }
    

    GCD 栅栏函数

    //0.获得全局并发队列
        //栅栏函数不能使用全局并发队列
        //dispatch_queue_t queue =  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
        
        //1.异步函数
        dispatch_async(queue, ^{
           
            for (NSInteger i = 0; i<100; i++) {
                NSLog(@"download1-%zd-%@",i,[NSThread currentThread]);
            }
            
        });
        
        dispatch_async(queue, ^{
            
            for (NSInteger i = 0; i<100; i++) {
                NSLog(@"download2-%zd-%@",i,[NSThread currentThread]);
            }
        });
        
        
        //栅栏函数
        dispatch_barrier_async(queue, ^{
           
            NSLog(@"+++++++++++++++++++++++++++++");
        });
        
        dispatch_async(queue, ^{
            
            for (NSInteger i = 0; i<100; i++) {
                NSLog(@"download3-%zd-%@",i,[NSThread currentThread]);
            }
        });
        
        dispatch_async(queue, ^{
            
            for (NSInteger i = 0; i<100; i++) {
                NSLog(@"download4-%zd-%@",i,[NSThread currentThread]);
            }
        });
    

    GCD 中的 apply

    //开子线程和主线程一起完成遍历任务,任务的执行时并发的
    -(void)apply
    {
        /*
         第一个参数:遍历的次数
         第二个参数:队列(并发队列)
         第三个参数:index 索引
         */
        dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
            NSLog(@"%zd---%@",index,[NSThread currentThread]);
        });
    }
    

    GCD 中的 group

    -(void)group1
    {
        //1.创建队列
        dispatch_queue_t queue =dispatch_get_global_queue(0, 0);
        
        //2.创建队列组
        dispatch_group_t group = dispatch_group_create();
        
        //3.异步函数
        /*
         1)封装任务
         2)把任务添加到队列中
         dispatch_async(queue, ^{
         NSLog(@"1----%@",[NSThread currentThread]);
         });
         */
        /*
         1)封装任务
         2)把任务添加到队列中
         3)会监听任务的执行情况,通知group
         */
        dispatch_group_async(group, queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
        });
        
        
        dispatch_group_async(group, queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        
        dispatch_group_async(group, queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
        });
        
        //拦截通知,当队列组中所有的任务都执行完毕的时候回进入到下面的方法
        dispatch_group_notify(group, queue, ^{
            
            NSLog(@"-------dispatch_group_notify-------");
        });
        
        //    NSLog(@"----end----");
    
    }
    
    -(void)group2
    {
        //1.创建队列
        dispatch_queue_t queue =dispatch_get_global_queue(0, 0);
        
        //2.创建队列组
        dispatch_group_t group = dispatch_group_create();
        
        //3.在该方法后面的异步任务会被纳入到队列组的监听范围,进入群组
        //dispatch_group_enter|dispatch_group_leave 必须要配对使用
        dispatch_group_enter(group);
        
        dispatch_async(queue, ^{
            NSLog(@"1----%@",[NSThread currentThread]);
            
            //离开群组
            dispatch_group_leave(group);
        });
        
        dispatch_group_enter(group);
        
        dispatch_async(queue, ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        
            //离开群组
            dispatch_group_leave(group);
        });
        
        
        //拦截通知
        //问题?该方法是阻塞的吗?  内部本身是异步的
    //    dispatch_group_notify(group, queue, ^{
    //        NSLog(@"-------dispatch_group_notify-------");
    //    });
        
        //等待.死等. 直到队列组中所有的任务都执行完毕之后才能执行
        //阻塞的
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        NSLog(@"----end----");
        
    }
    
    -(void)group3
    {
        /*
         1.下载图片1 开子线程
         2.下载图片2 开子线程
         3.合成图片并显示图片 开子线程
         */
        
        //-1.获得队列组
        dispatch_group_t group = dispatch_group_create();
        
        //0.获得并发队列
        dispatch_queue_t queue =  dispatch_get_global_queue(0, 0);
        
        // 1.下载图片1 开子线程
        dispatch_group_async(group, queue,^{
            
            NSLog(@"download1---%@",[NSThread currentThread]);
            //1.1 确定url
            NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1511338633697&di=294a5b722555e26ba1c5c08b60b4a8b7&imgtype=0&src=http%3A%2F%2Fimg.sucaifengbao.com%2Fvector%2Flogosjbz%2F31_376_bp.jpg"];
            
            //1.2 下载二进制数据
            NSData *imageData = [NSData dataWithContentsOfURL:url];
            
            //1.3 转换图片
            self.image1 = [UIImage imageWithData:imageData];
        });
        
        // 2.下载图片2 开子线程
         dispatch_group_async(group, queue,^{
             
             NSLog(@"download2---%@",[NSThread currentThread]);
             //2.1 确定url
            NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1511338633697&di=294a5b722555e26ba1c5c08b60b4a8b7&imgtype=0&src=http%3A%2F%2Fimg.sucaifengbao.com%2Fvector%2Flogosjbz%2F31_376_bp.jpg"];
            
            //2.2 下载二进制数据
            NSData *imageData = [NSData dataWithContentsOfURL:url];
            
            //2.3 转换图片
            self.image2 = [UIImage imageWithData:imageData];
        });
    
        //3.合并图片
        //主线程中执行
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
           
            NSLog(@"combie---%@",[NSThread currentThread]);
            //3.1 创建图形上下文
            UIGraphicsBeginImageContext(CGSizeMake(200, 200));
            
            //3.2 画图1
            [self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
            self.image1 = nil;
            
            //3.3 画图2
            [self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
            self.image2 = nil;
            
            //3.4 根据上下文得到一张图片
            UIImage *image =  UIGraphicsGetImageFromCurrentImageContext();
            
            //3.5 关闭上下文
            UIGraphicsEndImageContext();
            
            //3.6 更新UI
    //        dispatch_async(dispatch_get_main_queue(), ^{
            
                NSLog(@"UI----%@",[NSThread currentThread]);
                self.imageView.image = image;
    //        });
        });
        
    //    dispatch_release(group)
    }
    
    
    • NSOperation
      NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
      • 使用NSOperation子类的方式有3种
        NSInvocationOperation
        NSBlockOperation
        自定义子类继承NSOperation,实现内部相应的方法
    //1.创建操作,封装任务
        /*
         第一个参数:目标对象 self
         第二个参数:调用方法的名称
         第三个参数:前面方法需要接受的参数 nil
         */
     NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
         NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download2) object:nil];
         NSInvocationOperation *op3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download3) object:nil];
        
    或者
    
    //1.创建操作
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"1----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"2----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"3----%@",[NSThread currentThread]);
        }];
    
        //2.启动|执行操作
         [op1 start];
         [op2 start];
         [op3 start];
    
    //简便方法
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        //1)创建操作,2)添加操作到队列中
        [queue addOperationWithBlock:^{
            NSLog(@"7----%@",[NSThread currentThread]);
        }];
    
        //设置最大并发数量 maxConcurrentOperationCount
        //同一时间最多有多少个任务可以执行
        //串行执行任务!=只开一条线程 (线程同步)
        // maxConcurrentOperationCount >1 那么就是并发队列
        // maxConcurrentOperationCount == 1 那就是串行队列
        // maxConcurrentOperationCount == 0  不会执行任务
        // maxConcurrentOperationCount == -1 特殊意义 最大值 表示不受限制
    
    

    相关文章

      网友评论

          本文标题:多线程总结

          本文链接:https://www.haomeiwen.com/subject/cpenvxtx.html