美文网首页
多线程和网络1

多线程和网络1

作者: weyan | 来源:发表于2018-11-26 08:29 被阅读0次

    一、多线程

    1、进程:

    进程

    2、线程

    线程

    2.1、线程的串行

    线程的串行 进程和线程比较 多线程 多线程原理 多线程优缺点

    3、主线程

    主线程 判断主线程方法 主线程注意点

    4、 iOS中多线程的实施方案:

    iOS中多线程的实施方案

    ①pthread的使用

    pthread的使用
    #import "ViewController.h"
    #import <pthread.h>
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (IBAction)btnClick:(id)sender {
        
        NSLog(@"mainThread:%@",[NSThread mainThread]);
        
        //pthread创建线程,执行任务
        //01 包含头文件
        //02 创建线程对象
        
        pthread_t thread = nil;
        
        //03 创建线程,执行任务
        /* 参数说明
         *
         * 第一个参数:线程对象 传地址
         * 第二个参数:线程的属性 (优先级)
         * 第三个参数:指向函数的指针
         * 第四个参数:传给第三个参数的(参数)
         */
        pthread_create(&thread, NULL, run, NULL);
        
       
    }
    
    //技巧:(*)改写成函数的名称,补全参数
    void *run(void *str)
    {
        NSLog(@"run-------%@",[NSThread currentThread]);
        
        //把耗时操作放在子线程中执行
    //    for (int i = 0; i < 1000000; ++i) {
    //        NSLog(@"%d---%@",i,[NSThread  currentThread]);
    //    }
        
        return NULL;
    }
    
    @end
    

    ②NSThread的使用

    #import "ViewController.h"
    #import "XMGThread.h"
    
    @interface ViewController ()
    @end
    
    @implementation ViewController
    
    /* 知识点说明
     *
     * 01 线程创建的几种方法
     *    ① 直接alloc initWithTa...            代码量更大|能够拿到线程对象
     *    ② 分离出子线程   detachNewThread      无法拿到线程对象进行详细设置
     *    3  开启后台线程   InBackground         无法拿到线程对象进行详细设置
     * 02 设置属性(名称|优先级)
     * 03 生命周期:当线程内部的任务执行完毕会被释放
     **/
    
    #pragma mark ----------------
    #pragma mark Events
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        //创建线程对象
        XMGThread *threadA = [[XMGThread alloc]initWithTarget:self selector:@selector(run) object:nil];
        XMGThread *threadB = [[XMGThread alloc]initWithTarget:self selector:@selector(run) object:nil];
        XMGThread *threadC = [[XMGThread alloc]initWithTarget:self selector:@selector(run) object:nil];
        
         //设置线程的属性
        threadA.name = @"线程01";
        threadB.name = @"线程02";
        threadC.name = @"线程03";
        
        //设置线程的优先级  范围 0.0~1.0 默认是0.5  1.0最高的
        //优先级更高的线程,被CPU调度到的概率会更高
        threadA.threadPriority = 1.0 ;
        threadB.threadPriority = 0.1 ;
        
        //启动线程
        [threadA start];
        [threadB start];
        [threadC start];
        
        //线程的生命周期:从创建到释放
        //当线程内部的任务执行完毕的时候,线程对象会被自动释放
    }
    
    #pragma mark -----------------------
    #pragma mark 创建线程的几种方法
    -(void)createThread1{
        //01 创建线程对象
        /* 参数说明
         *
         * 第一个参数:目标对象
         * 第二个参数:方法选择器 要执行的任务(方法)
         * 第三个参数:调用函数需要传递的参数
         */
        NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
        
        //02 启动线程
        [thread start];
    }
    
    -(void)createThread2{
        //直接分离出一条子线程
        [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
    }
    
    -(void)createThread3{
        //开启后台线程
        [self performSelectorInBackground:@selector(run) withObject:nil];
    }
    
    -(void)run
    {
    //    NSLog(@"run----%@",[NSThread currentThread].name);
        for (int i = 0; i < 100; ++i) {
            NSLog(@"%zd---%@",i,[NSThread currentThread].name);
        }
    }
    @end
    

    线程状态

    线程状态 控制线程状态

    多线程的安全隐患

    多线程的安全隐患
    #import "ViewController.h"
    
    @interface ViewController ()
    
    //nonatomic 非原子属性  不安全(不会加锁) 性能好
    // atomic     原子属性  安全(内部会对setter方法加锁)
    
    @property (nonatomic, strong) NSThread *thread01;
    @property (nonatomic, strong) NSThread *thread02;
    @property (nonatomic, strong) NSThread *thread03;
    @property (nonatomic, assign) NSInteger totalCount;
    
    @property (nonatomic, strong) NSObject *lock;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad{
        [super viewDidLoad];
    
        self.lock = [[NSObject alloc]init];
        //设置总票数
        self.totalCount = 100;
        
        //初始化售票员(创建线程对象)
        self.thread01 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
        self.thread02 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
        self.thread03 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
        
        //设置名称
        self.thread01.name = @"售票员小妹A";
        self.thread02.name = @"售票员小妹B";
        self.thread03.name = @"售票员小妹C";
        
        //启动线程
        [self.thread01 start];
        [self.thread02 start];
        [self.thread03 start];
    }
    
    -(void)saleTicket{
        while (1) {
             //为代码添加同步锁(互斥锁)
             /*
              * token:锁对象 (要使用全局的对象) 建议直接使用self
              * {}   要加锁的代码段
              * 注意点:①加多把锁死无效的②要注意加锁的位置
              */
             //线程A
             //线程C  (排队)
             //线程B  如果锁对象是状态是关闭的,那么线程B进入阻塞状态(等待)
            //当锁打开之后,会主动唤醒排队的线程(B)
            @synchronized(self) {
                //售票 检查余票-如果有票卖出一张,否则提示用户
                NSInteger count = self.totalCount;
                if (count >0) {
                    //卖票
                    self.totalCount = count - 1;
                    for (int i = 0; i < 10000000; ++i) {
                        
                    }
                    NSLog(@"%@卖出去了一张票,还剩下%zd张票",[NSThread currentThread].name,self.totalCount);
                }else
                {
                    //提示用户票已经卖完
                    NSLog(@"%@发现票已经卖完啦",[NSThread currentThread].name);
                    break;
                }
            }
        }
    }
    
    @end
    

    原子和非原子属性的选择

    原子和非原子属性的选择

    线程间通信

    #import "ViewController.h"
    
    @interface ViewController ()
    @property (weak, nonatomic) IBOutlet UIImageView *imageView;
    
    @end
    
    @implementation ViewController
    
    /*
     App Transport Security Settings
        Allow Arbitrary Loads YES
     */
    
    #pragma mark -----------------------
    #pragma mark Events
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        //创建子线程(3种方法)
        [NSThread detachNewThreadSelector:@selector(download) toTarget:self withObject:nil];
    }
    
    #pragma mark -----------------------
    #pragma mark Methods
    
    -(void)download{
        NSLog(@"download----%@",[NSThread currentThread]);
        
        //01 确定URL地址
        NSURL *url = [NSURL URLWithString:@"http://image.tianjimedia.com/uploadImages/2015/083/30/VVJ04M7P71W2.jpg"];
        
        //02 把图片的二进制数据下载到本地
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        
        //03 把图片的二进制格式转换为UIimage
        UIImage *image = [UIImage imageWithData:imageData];
        
        //报错:把和UI相关的操作放在后台线程中处理
    
        //04 回到主线程显示图片
        //子线程切换回主线程
        /* 参数说明
         *
         * 第一个参数:方法选择器  回到主线程要做什么(方法)
         * 第二个参数:调用函数需要传递的参数
         * 第三个参数:是否等待该方法执行完毕才继续往下执行 YES:等待
         */
        //第一种方法
        //[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
    
        //第二种方法
        [self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
        
        //简便方法(imageView中有一个image属性,传个iamge参数会自动调用image的setter方法)
        //[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
        
        NSLog(@"_____end______");
    }
    
    -(void)showImage:(UIImage *)image{
        //NSLog(@"UI----%@",[NSThread currentThread]);
        for (int i = 0; i < 100; ++i) {
            NSLog(@"%zd---",i);
        }
        self.imageView.image = image;
    }
    
    #pragma mark -----------------------
    #pragma mark 计算代码段执行时间的两种方法
    //方法1
    -(void)timer{
        NSDate *start = [NSDate date];  //获得当前的时间
        //01 确定URL地址
        NSURL *url = [NSURL URLWithString:@"http://image.tianjimedia.com/uploadImages/2015/083/30/VVJ04M7P71W2.jpg"];
        NSDate *end = [NSDate date];  //获得当前的时间
        NSLog(@"%f",[end timeIntervalSinceDate:start]);
      
        //02 把图片的二进制数据下载到本地
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        
        //03 把图片的二进制格式转换为UIimage
        UIImage *image = [UIImage imageWithData:imageData];
        
        //04 显示图片
        self.imageView.image = image;
    }
    
    //方法2
    -(void)timer2{
        CFTimeInterval start = CFAbsoluteTimeGetCurrent();//获得当前时间(相对时间)
        
        //01 确定URL地址
        NSURL *url = [NSURL URLWithString:@"http://image.tianjimedia.com/uploadImages/2015/083/30/VVJ04M7P71W2.jpg"];
        CFTimeInterval end = CFAbsoluteTimeGetCurrent();//获得当前时间(相对时间)
        NSLog(@"%f",end - start);
        
        //02 把图片的二进制数据下载到本地
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        
        //03 把图片的二进制格式转换为UIimage
        UIImage *image = [UIImage imageWithData:imageData];
        
        //04 显示图片
        self.imageView.image = image;
    }
    @end
    

    ③GCD使用

    1、GCD简介

    GCD简介

    2、任务和队列

    任务和队列 执行任务的方法 队列类型 容易混淆的术语 并发队列一 并发队列二 串行队列 总结1

    3、GCD的几种组合
    3.1、异步函数+并行队列

    //异步函数 + 并发队列:会开启多条子线程,所有的任务并发执行
    //注意:开几条线程并不是由任务的数量决定的,是有GCD内部自动决定的
    -(void)asyncConcurrent{
        //01 创建队列
        /* 参数说明
         * 第一个参数:C语言的字符串 给队列起个名字(建议:com.520it.www.DownloadQueue)
         * 第二个参数:类型
         *          DISPATCH_QUEUE_CONCURRENT  并发队列
         *          DISPATCH_QUEUE_SERIAL      串行队列
         */
        
        //dispatch_queue_t queue = dispatch_queue_create("com.520it.www.DownloadQueue", DISPATCH_QUEUE_CONCURRENT);
    
        //DISPATCH_QUEUE_PRIORITY_DEFAULT == 0 默认优先级
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
         NSLog(@"-----start----");
        
        //02 封装任务,把任务添加到队列
        dispatch_async(queue, ^{
            NSLog(@"1-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"2-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"3-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"4-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"5-----%@",[NSThread currentThread]);
        });
        
        NSLog(@"-----end----");
    }
    
    异步函数 + 并发队列执行结果

    3.2、异步函数 + 串行队列

    //异步函数 + 串行队列:会开启一条子线程,所有的任务在该子线程中串行执行
    -(void)asyncSerial{
        //01 创建队列
        /* 参数说明
         * 第一个参数:C语言的字符串 给队列起个名字(建议:com.520it.www.DownloadQueue)
         * 第二个参数:类型
         *          DISPATCH_QUEUE_CONCURRENT  并发队列
         *          DISPATCH_QUEUE_SERIAL      串行队列
         */
        dispatch_queue_t queue = dispatch_queue_create("com.520it.www.DownloadQueue", DISPATCH_QUEUE_SERIAL);
        
        
        //02 封装任务,把任务添加到队列
        dispatch_async(queue, ^{
            NSLog(@"1-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"2-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"3-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"4-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"5-----%@",[NSThread currentThread]);
        });
        
    }
    
    异步函数 + 串行队列执行结果

    3.3、同步函数 + 并发队列

    //同步函数 + 并发队列:不会开启子线程,所有的任务在当前线程中串行执行
    -(void)syncConcurrent{
        //01 创建队列
        /* 参数说明
         * 第一个参数:C语言的字符串 给队列起个名字(建议:com.520it.www.DownloadQueue)
         * 第二个参数:类型
         *          DISPATCH_QUEUE_CONCURRENT  并发队列
         *          DISPATCH_QUEUE_SERIAL      串行队列
         */
        dispatch_queue_t queue = dispatch_queue_create("com.520it.www.DownloadQueue", DISPATCH_QUEUE_CONCURRENT);
        
        NSLog(@"-----start----");
        //02 封装任务,把任务添加到队列
        dispatch_sync(queue, ^{
            NSLog(@"1-----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"2-----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"3-----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"4-----%@",[NSThread currentThread]);
        });
        
       NSLog(@"-----end----");
        
    }
    
    同步函数+并发队列执行结果

    ** 3.4、同步函数+串行队列**

    //同步函数 + 串行队列:不会开启子线程,所有的任务在当前线程中串行执行
    -(void)syncSerial{
        //01 创建队列
        /* 参数说明
         * 第一个参数:C语言的字符串 给队列起个名字(建议:com.520it.www.DownloadQueue)
         * 第二个参数:类型
         *          DISPATCH_QUEUE_CONCURRENT  并发队列
         *          DISPATCH_QUEUE_SERIAL      串行队列
         */
        dispatch_queue_t queue = dispatch_queue_create("com.520it.www.DownloadQueue", DISPATCH_QUEUE_SERIAL);
        
        
        //02 封装任务,把任务添加到队列
        dispatch_sync(queue, ^{
            NSLog(@"1-----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"2-----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"3-----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"4-----%@",[NSThread currentThread]);
        });
        
    }
    
    同步函数+串行队列执行结果

    3.5、异步函数+主队列

    //异步函数 + 主队列:不会开线程,所有的任务都在主线程中串行执行
    -(void)asyncMain{
        NSLog(@"start-------");
        //01 获得主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        //02 封装任务并把任务添加到队列
        dispatch_async(queue, ^{
            NSLog(@"1-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"2-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"3-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"4-----%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"5-----%@",[NSThread currentThread]);
        });
      NSLog(@"end------");
    }
    
    异步函数+主队列执行结果

    3.6、同步+主队列

    //同步函数 + 主队列:会发生“死锁”
    -(void)syncMain{
        //01 获得主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        NSLog(@"--------");
        //02 封装任务并把任务添加到队列
        dispatch_sync(queue, ^{
            NSLog(@"1-----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"2-----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"3-----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"4-----%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"5-----%@",[NSThread currentThread]);
        });
        
    }
    
    同步函数 + 主队列执行结果

    为什么同步函数+主队列会发生死锁,而异步函数+主队列不会呢?

    1、主队列中的任务必须在主线程中执行,当主队列中有任务的时候,主队列就会安排主线程来执行该任务,但在调度之前会先检查主线程的状态(是否在忙),如果主线程当前在忙,那么就会暂停调度,直到主线程空闲为止。
    2、同步函数+主队列:会发生死锁是因为调用synMain函数在主线程发现主线程在执行任务,在调度主队列中的任务时发现主线程在忙就暂停调度。(因为同步函数是按顺序执行任务的,执行完一个任务后再执行下一个任务),"解决方式:在子线程中调用synMain就不会发生死锁的现象了"
    3、异步函数+主队列:不会发生死锁是因为调用asynMain函数在主线程发现主线程在执行任务,因为是“异步函数”在执行任务时是按照异步方式执行的,发现在主队列中有需要等待执行的任务时就先跳过,执行下面的任务,等空闲的时候再去调度主队列中的任务。

    总结2

    4、GCD实现线程间的通信

    在子线程下载图片,然后回到主线程显示图片

    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        //GCD中的队列:串行(2)|并发(2)
        //01 创建队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        //02 封装下载图片的任务,并且添加到队列
        //同步函数|异步函数
        dispatch_async(queue, ^{
           
            //03 URL
            NSURL *url = [NSURL URLWithString:@"http://article.fd.zol-img.com.cn/t_s500x2000/g4/M08/04/06/Cg-4WlPoSQaIJMNHAAFQxnn-qSoAAQZGQCqggkAAVDe827.jpg"];
            
            //04 下载图片的二进制数据到本地
            NSData *imageData= [NSData dataWithContentsOfURL:url];
            
            //05 转换格式
            UIImage *image = [UIImage imageWithData:imageData];
            
            NSLog(@"Download-----%@",[NSThread currentThread]);
            
            //回到主线程设置图片 主队列 + 同步函数|异步函数
            dispatch_sync(dispatch_get_main_queue(), ^{
               
                //设置图片
                self.imageView.image = image;
                 NSLog(@"UI-----%@",[NSThread currentThread]);
            });
        });
    }t
    

    5、GCD一次性代码

    GCD一次性代码

    6、GCD的延迟执行

    GCD的延迟执行

    7、GCD快速迭代(遍历)

    GCD快速迭代(遍历) 快速迭代的应用 快速迭代应用执行结果

    8、GCD栅栏函数

    GCD栅栏函数 GCD栅栏函数执行结果

    9、GCD队列组

    队列组的优势:在多个

    **---------------第一种使用方法-------------------**
    -(void)group{
        //00 创建队列组
        dispatch_group_t group = dispatch_group_create();
        
        //01 获得并发队列
        dispatch_queue_t queue = dispatch_queue_create("Test", DISPATCH_QUEUE_CONCURRENT);
        dispatch_queue_t queue2 = dispatch_queue_create("Test2", DISPATCH_QUEUE_CONCURRENT);
        
        //02 封装任务,添加到队列并监听任务的执行情况
        /*
         //01 封装任务
         //02 把任务添加到队列
         //03 监听任务的执行情况
        dispatch_group_async(group, queue, ^{
            
        });
         */
        //采用block方式封装任务
        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_async(group, queue2, ^{
    
            NSLog(@"4---%@",[NSThread currentThread]);
        });
        
          /*dispatch_group_async(group, queue2, ^{
            NSLog(@"5---%@",[NSThread currentThread]);
        });
           */
         //封装任务(函数)
        dispatch_async_f(queue, NULL, run);//函数名run就是函数指针
    
        //03 拦截通知,当所有的任务都执行完毕后,执行++++操作
        //group:拦截上面的任务,所有任务执行完后执行该block任务
        //queue:决定该block在哪个线程中处理(主队列:主线程 非主队列:子线程)
        //dispatch_group_notify 内部是异步的执行
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"++++++++%@",[NSThread currentThread]);
        });
        
        NSLog(@"---end---%@",[NSThread currentThread]);
    }
    //void (*dispatch_function_t)(void *_Nullable);
    //1.用函数名代替(*dispatch_function_t) | 2.添加参数i
    void run(void *str)
    {
       NSLog(@"5---%@",[NSThread currentThread]);
    }
    
    **--------------------第二种使用方法----------------------**
    -(void)group2{
        //00 创建队列组
        dispatch_group_t group = dispatch_group_create();
        
        //01 获得并发队列
        dispatch_queue_t queue = dispatch_queue_create("Test", DISPATCH_QUEUE_CONCURRENT);
      
        /*该方法相当于下面的写法
        dispatch_group_async(group, queue, ^{
            NSLog(@"1---%@",[NSThread currentThread]);
        });
         */
        
        //在该方法后面的任务会被队列组监听
        //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_enter(group);
        dispatch_async(queue, ^{
            NSLog(@"3----%@",[NSThread currentThread]);
            dispatch_group_leave(group);
        });
        
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            NSLog(@"4----%@",[NSThread currentThread]);
            dispatch_group_leave(group);
        });
    
        //03 拦截通知,
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"++++++++%@",[NSThread currentThread]);
        });
        
    }
    
    GCD队列组

    队列组的应用

    //需求:开子线程下载两张图片,合成图片,显示出来
    -(void)group3{
       
        //01 创建队列组
        dispatch_group_t group = dispatch_group_create();
        
        //02 获得并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        //03 下载图片1
        dispatch_group_async(group, queue, ^{
            
            //001 url
            NSURL *URL = [NSURL URLWithString:@"http://pridelands.ru/pictures/real/puma/Cougar_018.jpg"];
            
            //002 Data
            NSData *imageData = [NSData dataWithContentsOfURL:URL];
            
            //003 转换
            self.image1 = [UIImage imageWithData:imageData];
            
              NSLog(@"Download1-----%@",[NSThread currentThread]);
        });
        
        //04 下载图片2
        dispatch_group_async(group, queue, ^{
            
            //001 url
            NSURL *URL = [NSURL URLWithString:@"http://m2.quanjing.com/2m/pust017/1574r-012067.jpg"];
            
            //002 Data
            NSData *imageData = [NSData dataWithContentsOfURL:URL];
            
            //003 转换
            self.image2 = [UIImage imageWithData:imageData];
            
              NSLog(@"Download2-----%@",[NSThread currentThread]);
        });
        
        //05 拦截通知,合成图片
        dispatch_group_notify(group, queue, ^{
            
            //001 开启上下文
            UIGraphicsBeginImageContext(CGSizeMake(300, 300));
            
            //002 画图1,2
            [self.image1 drawInRect:CGRectMake(0, 0, 150, 300)];
            [self.image2 drawInRect:CGRectMake(150, 0, 150, 300)];
            
            NSLog(@"Combie-----%@",[NSThread currentThread]);
            //003 根据上下文得到图片
            UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
            
            //004 关闭上下文
            UIGraphicsEndImageContext();
            
            //06 显示图片(线程间通信)
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imageView.image = image;
                NSLog(@"UI-----%@",[NSThread currentThread]);
            });
        });  
    }
    

    使用Create函数创建的并发队列和全局并发队列的主要区别

    使用Create函数创建的并发队列和全局并发队列的主要区别

    ④NSOperation

    NSOperation简介 NSOperation子类

    1、封装操作的两种方式

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    #pragma mark -----------------------
    #pragma mark Events
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        
        //NSOperation :抽象类
        //NSInvocationOperation
        //NSBlockOperation
        
        //[self invocationOperation];
        [self blockOperation];
    }
    
    #pragma mark -----------------------
    #pragma mark 封装操作的两种方式
    -(void)invocationOperation{
        //01 封装操作对象
        NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
        
        NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
        
        
        NSInvocationOperation *op3 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
        
        //02 执行操作
         [op1 start];
         [op2 start];
         [op3 start];
    }
    
    -(void)blockOperation{
        //操作:NSBlockOperation对象
        //任务:block
        
        //01 封装操作对象
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"1----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"2----%@",[NSThread currentThread]);
        }];
        
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"3----%@",[NSThread currentThread]);
        }];
        
        //追加任务
        //当一个操作中的任务数量>1的时候,就会开启子线程和当前线程一起执行任务
        [op3 addExecutionBlock:^{
            NSLog(@"4----%@",[NSThread currentThread]);
        }];
        
        [op3 addExecutionBlock:^{
            NSLog(@"5----%@",[NSThread currentThread]);
        }];
        
        //02 执行操作
        [op1 start];
        [op2 start];
        [op3 start];
        
    }
    
    #pragma mark -----------------------
    #pragma mark Methods
    -(void)download1{
        NSLog(@"download1---%@",[NSThread currentThread]);
    }
    
    @end
    

    2、操作队列的基本使用

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    #pragma mark -----------------------
    #pragma mark Events
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
        //GCD队列:并发(自己创建|全局并发)|串行(自己创建|主队列)
        //操作队列:自定义队列|主队列
        /*
         自定义队列:[[NSOperationQueue alloc]init]
              特点:默认并发队列,但是可以控制让它变成一个串行队列。
        主队列:[NSOperationQueue mainQueue]
            特点:串行队列,和主线程相关(凡是放在主队列中的任务的都在主线程中执行)。
         */
    //    [self invocationOperationWithQueue];
    //    [self blockOperationWithQueue];
        [self changeSerialQueue];
    }
    
    #pragma mark -----------------------
    #pragma mark -- 操作队列的基本使用(操作 + 队列)
    
    -(void)invocationOperationWithQueue{
        //01 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        //02 封装操作
        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];
        
        //03 把操作添加到队列
        [queue addOperation:op1];  //该方法内部会自动的调用start方法执行任务
        [queue addOperation:op2];
        [queue addOperation:op3];
        
        //注意:开启几条子线程并不是由操作的数量决定的
    }
    
    -(void)blockOperationWithQueue{
        //01 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //02 封装操作对象
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"1----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"2----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"3----%@",[NSThread currentThread]);
        }];
        
        //03 把操作添加到队列中
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
        
        //简便方法:该方法内部首先会把block中的任务封装成一个操作(Operation),然后把该操作直接添加到队列
        [queue addOperationWithBlock:^{
            NSLog(@"4----%@",[NSThread currentThread]);
        }];
        
    }
    
    #pragma mark -----------------------
    #pragma mark 最大并发数
    -(void)changeSerialQueue{
        //01 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //02 封装操作对象
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"1----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"2----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"3----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"4----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"5----%@",[NSThread currentThread]);
        }];
        
        //设置最大并发数对于任务数量大于1的操作是无效的
        //当操作中任务数量>1的时候,会开启多条子线程和当前线程一起工作
        [op5 addExecutionBlock:^{
            NSLog(@"6----%@",[NSThread currentThread]);
        }];
       
        [op5 addExecutionBlock:^{
            NSLog(@"7----%@",[NSThread currentThread]);
        }];
        
        //设置最大并发数 == 同一时间最多有多少条线程在执行
        //maxConcurrentOperationCount == 0 不能执行任务
        //NSOperationQueueDefaultMaxConcurrentOperationCount = -1 -1指的是一个最大的值(表示不受限制)
        queue.maxConcurrentOperationCount = 1;
        
        //03 把操作添加到队列中
    //    [queue addOperation:op1];
    //    [queue addOperation:op2];
    //    [queue addOperation:op3];
    //    [queue addOperation:op4];
    //    [queue addOperation:op5];
        
        [queue addOperations:@[op1,op2,op3,op4,op5] waitUntilFinished:YES];
        NSLog(@"你是狗吗");
    }
    
    #pragma mark -----------------------
    #pragma mark Methods
    -(void)download1{
        NSLog(@"download1---%@",[NSThread currentThread]);
    }
    -(void)download2{
        NSLog(@"download2---%@",[NSThread currentThread]);
    }
    -(void)download3{
        NSLog(@"download3---%@",[NSThread currentThread]);
    }
    
    @end
    

    3、操作队列其他用法

    -----------------自定义NSOperation----------------
    
    1、--------------------------自定义XMGOperation--------------------------
    #import <Foundation/Foundation.h>
    
    @interface XMGOperation : NSOperation
    
    @end
    
    #import "XMGOperation.h"
    
    @implementation XMGOperation
    
    //重写内部的main方法类告诉自定义的操作任务是什么?
    -(void)main{
        // NSLog(@"main----%@",[NSThread currentThread]);
        for (int i = 0; i < 10000; ++i) {
            NSLog(@"1---%d--%@",i,[NSThread currentThread]);
        }
        
        //官方建议:在自定义操作的时候每执行完一个耗时操作就判断一下当前操作是否被取消,如果被取消就直接返回。
        if (self.isCancelled) {
            return;
        }
        
        NSLog(@"++++++++++++++++");
        
        for (int i = 0; i < 10000; ++i) {
            NSLog(@"2---%d--%@",i,[NSThread currentThread]);
        }
        
        if (self.isCancelled) {
            return;
        }
         NSLog(@"++++++++++++++++");
        
        for (int i = 0; i < 10000; ++i) {
            NSLog(@"3d---%d--%@",i,[NSThread currentThread]);
        }
        
    }
    
    @end
    
    -----------------------控制器----------------------
    #import "ViewController.h"
    #import "XMGOperation.h"//自定义NSOperation
    #import "XMGThread.h"//自定义Thread
    
    @interface ViewController ()
    @property (nonatomic, strong) NSOperationQueue *queue;
    @end
    
    @implementation ViewController
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        
    }
    
    -(void)thread{
        //自定义线程
        XMGThread *thread = [[XMGThread alloc] init];
        [thread start];
    }
    
    //开始按钮
    - (IBAction)startBtnClick:(id)sender {
        
    //    [self test1];
        [self test2];
        
    }
    
    //暂停按钮
    - (IBAction)suspendBtnClick:(id)sender {
        
        //暂停 YES
        //只能暂停当前操作后面的操作,当前操作不可分割必须执行完毕
        //操作是有状态的
        [self.queue setSuspended:YES];
    }
    
    //恢复按钮
    - (IBAction)resumeBtnClick:(id)sender {
        
        //恢复
        [self.queue setSuspended:NO];
    }
    
    //取消按钮
    - (IBAction)cancelBtnClick:(id)sender {
        
        //取消 取消所有的操作
        //只能取消队列中处于等待状态的操作
        [self.queue cancelAllOperations];
    }
    
    -(void)test1{
        //01 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //02 封装操作
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0; i < 5000; ++i) {
                NSLog(@"1--%d--%@",i,[NSThread currentThread]);
            }
            
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0; i < 5000; ++i) {
                NSLog(@"2--%d--%@",i,[NSThread currentThread]);
            }
        }];
        
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0; i < 5000; ++i) {
                NSLog(@"3--%d--%@",i,[NSThread currentThread]);
            }
        }];
        
        
        NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0; i < 5000; ++i) {
                NSLog(@"4--%d--%@",i,[NSThread currentThread]);
            }
        }];
        
        //设置最大并发数
        queue.maxConcurrentOperationCount = 1;
        
        //03 添加到队列
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
        [queue addOperation:op4];
        
        self.queue = queue;
    }
    
    
    #pragma mark-----自定义NSOperation
    -(void)test2{
        
        //01 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        
        //02 封装操作(执行任务)
        XMGOperation *op = [[XMGOperation alloc]init];
        
        //03 把操作添加到队列
        [queue addOperation:op];   //内部会调用start方法 ->main方法
        
        //自定义操作的好处:代码复用
        self.queue = queue;
    }
    @end
    

    4、操作依赖和监听

    #import "ViewController.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        
        //01 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc]init];
        NSOperationQueue *queue2 = [[NSOperationQueue alloc]init];
        
        //02 封装操作对象
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"1----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"2----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"3----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"4----%@",[NSThread currentThread]);
        }];
        
        NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"5----%@",[NSThread currentThread]);
        }];
        
        //监听任务执行完毕
        op4.completionBlock = ^{
            
            NSLog(@"主人,你的电影已经下载好了,快点来看我吧");
        };
        
        
        //03 设置操作依赖:4->3->2->1->5
        //️ 不能设置循环依赖,结果就是两个任务都不会执行
        //不同队列中的操作可以设置依赖
        [op5 addDependency:op1];
        [op1 addDependency:op2];
        //[op2 addDependency:op1];
        [op2 addDependency:op3];
        [op3 addDependency:op4];
        
        //04 把操作添加到队列
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
        [queue addOperation:op4];
        [queue2 addOperation:op5];
        
    }
    @end
    

    5、操作队列实现线程间通信

    操作队列实现线程间通信

    GCD和NSOperation简单对比

    GCD和NSOperation简单对比

    int

    相关文章

      网友评论

          本文标题:多线程和网络1

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