zoukankan      html  css  js  c++  java
  • IOS之线程

    一、NSThread

    1.线程阻塞

      - (IBAction)btnClick {
        NSThread *current = [NSThread currentThread];
        
        for (int i = 0; i<20000; i++) {
            NSLog(@"run---%@", current);
        }
        
        return NULL;

     }

    这样会阻塞当前主线程,即UI线程,导致界面死了

      void *run(void *data) {
        
        NSThread *current = [NSThread currentThread];
        
        for (int i = 0; i<20000; i++) {
            NSLog(@"run---%@", current);
        }
        
        return NULL;
    }

    - (IBAction)btnClick {
        // 1.获得当前的线程
        NSThread *current = [NSThread currentThread];
        NSLog(@"btnClick---%@", current);

        // 2.执行一些耗时操作 : 创建一条子线程
        pthread_t threadId;
        pthread_create(&threadId, NULL, run, NULL);
    }

     这样另外开启一个线程执行任务,就不会阻塞主线程了。

    2.NSThread使用的三种方式


    - (IBAction)btnClick {
        // 1.获得当前的线程
        NSThread *current = [NSThread currentThread];
        NSLog(@"btnClick---%@", current);
        
    //    NSThread *main = [NSThread mainThread];
    //    NSLog(@"btnClick---%@", main);
        
        // 2.执行一些耗时操作 : 创建一条子线程
        [self threadCreate];
    }

    - (void)run:(NSString *)param
    {
        NSThread *current = [NSThread currentThread];
        
        //for (int i = 0; i<10000; i++) {
            NSLog(@"%@----run---%@", current, param);
        //}
    }

    /**
     * NSThread的创建方式
     * 隐式创建线程, 并且直接(自动)启动
     */
    - (void)threadCreate3
    {
        // 在后台线程中执行 === 在子线程中执行
        [self performSelectorInBackground:@selector(run:) withObject:@"abc参数"];
    }

    /**
     * NSThread的创建方式
     * 创建完线程直接(自动)启动
     */
    - (void)threadCreate2
    {
        [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是参数"];
    }

    /**
     * NSThread的创建方式
     * 1> 先创建初始化线程
     * 2> start开启线程
     */
    - (void)threadCreate
    {
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"哈哈"];
        thread.name = @"线程A";
        // 开启线程
        [thread start];
        
        NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"哈哈"];
        thread2.name = @"线程B";
        // 开启线程
        [thread2 start];
    }

    3.线程状态

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil];
        self.thread.name = @"线程A";
    }

    - (void)test
    {
        NSLog(@"test - 开始 - %@", [NSThread currentThread].name);
        
    //    [NSThread sleepForTimeInterval:5]; // 阻塞状态
        
    //    NSDate *date = [NSDate dateWithTimeIntervalSinceNow:5.0];
    //    [NSThread sleepUntilDate:date];
        
        for (int i = 0; i<1000; i++) {
            NSLog(@"test - %d - %@", i, [NSThread currentThread].name);
            
            if (i == 50) {
    //            [NSThread exit];
            }
        }
        
        NSLog(@"test - 结束 - %@", [NSThread currentThread].name);
    }

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        // 开启线程
        [self.thread start];
    }

    4.线程同步

    /** 剩余票数 */
    @property (nonatomic, assign) int leftTicketsCount;
    @property (nonatomic, strong) NSThread *thread0;
    @property (nonatomic, strong) NSThread *thread1;
    @property (nonatomic, strong) NSThread *thread2;
    @end

    @implementation HMViewController

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        // 默认有100张
        self.leftTicketsCount = 100;
        
        // 开启多条线程同时卖票
        self.thread0 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
        self.thread0.name = @"售票员 A";
    //    self.thread0.threadPriority = 0.0;
        
        self.thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
        self.thread1.name = @"售票员 B";
    //    self.thread1.threadPriority = 1.0;
        
        self.thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
        self.thread2.name = @"售票员 C";
    //    self.thread2.threadPriority = 0.0;
    }

    /**
     * 卖票
     */
    - (void)saleTicket
    {
        while (1) {
            @synchronized(self) { // 加锁(只能用一把锁)
                // 1.先检查票数
                int count = self.leftTicketsCount;
                if (count > 0) {
                    // 暂停
    //                [NSThread sleepForTimeInterval:0.0002];
                    
                    // 2.票数 - 1
                    self.leftTicketsCount = count - 1;
                    
                    NSThread *current = [NSThread currentThread];
                    NSLog(@"%@ 卖了一张票, 剩余%d张票", current.name, self.leftTicketsCount);
                } else {
                    // 退出线程
                    [NSThread exit];
                }
            } // 解锁
        }
    }

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        [self.thread0 start];
        [self.thread1 start];
        [self.thread2 start];
    }

    这里模拟四个窗口买票,多线程之间执行任务是异步的,故要让线程同步,这里通过加锁的方式实现线程同步,是数据准确。通过@synchronized{}实现加锁

    5.线程之间进行通信

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        // 在子线程中调用download方法下载图片
        [self performSelectorInBackground:@selector(download) withObject:nil];
    }

    /**
     * 下载图片 : 子线程
     */
    - (void)download
    {
        // 1.根据URL下载图片
        NSURL *url = [NSURL URLWithString:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
        NSLog(@"-------begin");
        NSData *data = [NSData dataWithContentsOfURL:url]; // 这行会比较耗时
        NSLog(@"-------end");
        UIImage *image = [UIImage imageWithData:data];
        
        // 2.回到主线程显示图片
       // [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
        // setImage: 1s
        [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
    //    [self performSelectorOnMainThread:@selector(settingImage:) withObject:image waitUntilDone:NO];
    }

    /**
     * 设置(显示)图片: 主线程
     */
    //- (void)settingImage:(UIImage *)image
    //{
    //    self.imageView.image = image
    //}

    二、GCD

      1.基本使用

      - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        [self performSelectorInBackground:@selector(test) withObject:nil];
        
    //    [self syncMainQueue];
    }

    - (void)test
    {
        NSLog(@"test --- %@", [NSThread currentThread]);
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            NSLog(@"任务 --- %@", [NSThread currentThread]);
        });
    }

    /**
     * 使用dispatch_async异步函数, 在主线程中往主队列中添加任务
     */
    - (void)asyncMainQueue
    {
        // 1.获得主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        // 2.添加任务到队列中 执行
        dispatch_async(queue, ^{
            NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
        });
    }

    /**
     * 使用dispatch_sync同步函数, 在主线程中往主队列中添加任务 : 任务无法往下执行
     */
    - (void)syncMainQueue
    {
        // 1.获得主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        // 2.添加任务到队列中 执行
        dispatch_sync(queue, ^{
            NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
        });
    //    dispatch_sync(queue, ^{
    //        NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
    //    });
    //    dispatch_sync(queue, ^{
    //        NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
    //    });
        
        // 不会开启新的线程, 所有任务在主线程中执行
    }

    // 凡是函数名种带有createcopy ew etain等字眼, 都需要在不需要使用这个数据的时候进行release
    // GCD的数据类型在ARC环境下不需要再做release
    // CF(Core Foundation)的数据类型在ARC环境下还是需要再做release

    /**
     * 用dispatch_sync同步函数往串行列中添加任务
     */
    - (void)syncSerialQueue
    {
        // 1.创建串行队列
        dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL);
        
        // 2.添加任务到队列中 执行
        dispatch_sync(queue, ^{
            NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
        });
        
        // 3.释放资源
    //    dispatch_release(queue);   // MRC(非ARC)
        
        // 总结: 不会开启新的线程
    }

    /**
     * 用dispatch_sync同步函数往并发队列中添加任务
     */
    - (void)syncGlobalQueue
    {
        // 1.获得全局的并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        // 2.添加任务到队列中 执行
        dispatch_sync(queue, ^{
            NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
        });
        
        // 总结: 不会开启新的线程, 并发队列失去了并发的功能
    }

    /**
     * 用dispatch_async异步函数往串行队列中添加任务
     */
    - (void)asyncSerialQueue
    {
        // 1.创建串行队列
        dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL);
        
        // 2.添加任务到队列中 执行
        dispatch_async(queue, ^{
            NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
        });
        
        // 总结: 只开1个线程执行任务
    }

    /**
     * 用dispatch_async异步函数往并发队列中添加任务
     */
    - (void)asyncGlobalQueue
    {
        // 1.获得全局的并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        // 2.添加任务到队列中 执行
        dispatch_async(queue, ^{
            NSLog(@"----下载图片1-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"----下载图片2-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"----下载图片3-----%@", [NSThread currentThread]);
        });
        
        // 总结: 同时开启了3个线程
    }

     //主队列

     dispatch_queue_t queue = dispatch_get_main_queue();

     同步函数dispatch_sync    主队列中添加任务   开启一个线程执行任务

     异步函数dispatch_async  主队列中添加任务   不会开启新的线程, 所有任务在主线程中执行

     //创建的串行队列

      dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL);

     //全局的并发队列
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

     同步函数dispatch_sync    串行队列中添加任务      不会开启新的线程

     同步函数dispatch_sync    全局的并发队列中添加任务     不会开启新的线程, 并发队列失去了并发的功能

     异步函数dispatch_async   串行队列中添加任务      只开1个线程执行任务

     异步函数dispatch_async  全局的并发队列中添加任务    有几个任务开启几个线程

     2.GCD下载图片

      - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(queue, ^{
            NSLog(@"--download--%@", [NSThread currentThread]);
            // 下载图片
            NSURL *url = [NSURL URLWithString:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
            NSData *data = [NSData dataWithContentsOfURL:url]; // 这行会比较耗时
            UIImage *image = [UIImage imageWithData:data];
            
            // 回到主线程显示图片
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"--imageView--%@", [NSThread currentThread]);
                self.imageView.image = image;
            });
        });
    }

    3.其他用法(下载两张图片合并为一张)

      @interface HMViewController ()
    @property (weak, nonatomic) IBOutlet UIImageView *imageView1;
    @property (weak, nonatomic) IBOutlet UIImageView *imageView2;
    @property (weak, nonatomic) IBOutlet UIImageView *bigImageView;
    @end

    @implementation HMViewController

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        // 图片1: http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg
        // 图片2: http://news.baidu.com/z/resource/r/image/2014-06-22/b2a9cfc88b7a56cfa59b8d09208fa1fb.jpg
        /**
         1.下载图片1和图片2
         
         2.将图片1和图片2合并成一张图片后显示到imageView上
         
         思考:
         * 下载图片 : 子线程
         * 等2张图片都下载完毕后, 才回到主线程
         */
    }

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        // 创建一个组
        dispatch_group_t group = dispatch_group_create();
        
        // 开启一个任务下载图片1
        __block UIImage *image1 = nil;
        dispatch_group_async(group, global_queue, ^{
            image1 = [self imageWithURL:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
        });
        
        // 开启一个任务下载图片2
        __block UIImage *image2 = nil;
        dispatch_group_async(group, global_queue, ^{
            image2 = [self imageWithURL:@"http://news.baidu.com/z/resource/r/image/2014-06-22/b2a9cfc88b7a56cfa59b8d09208fa1fb.jpg"];
        });
        
        // 同时执行下载图片1下载图片2操作
        
        // 等group中的所有任务都执行完毕, 再回到主线程执行其他操作
        dispatch_group_notify(group, main_queue, ^{
            self.imageView1.image = image1;
            self.imageView2.image = image2;
            
            // 合并
            UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
            [image1 drawInRect:CGRectMake(0, 0, 100, 100)];
            [image2 drawInRect:CGRectMake(100, 0, 100, 100)];
            self.bigImageView.image = UIGraphicsGetImageFromCurrentImageContext();
            // 关闭上下文
            UIGraphicsEndImageContext();
        });
    //    if (self.log == NO) {
    //        NSLog(@"-------touchesBegan");
    //        self.log = YES;
    //    }
    //    static dispatch_once_t onceToken;
    //    dispatch_once(&onceToken, ^{
    //        NSLog(@"-------touchesBegan");
    //    });
    }

    - (void)downlaod2image
    {
        dispatch_async(global_queue, ^{
            NSLog(@"下载图片---%@", [NSThread currentThread]);
            
            // 下载图片1
            UIImage *image1 = [self imageWithURL:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
            NSLog(@"下载完图片1---%@", [NSThread currentThread]);
            // 下载图片2
            UIImage *image2 = [self imageWithURL:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];
            NSLog(@"下载完图片2---%@", [NSThread currentThread]);
            
            dispatch_async(main_queue, ^{
                NSLog(@"显示图片---%@", [NSThread currentThread]);
                
                self.imageView1.image = image1;
                self.imageView2.image = image2;
                
                // 合并
                UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 100), NO, 0.0);
                [image1 drawInRect:CGRectMake(0, 0, 100, 100)];
                [image2 drawInRect:CGRectMake(100, 0, 100, 100)];
                self.bigImageView.image = UIGraphicsGetImageFromCurrentImageContext();
                // 关闭上下文
                UIGraphicsEndImageContext();
            });
        });
    }

    - (UIImage *)imageWithURL:(NSString *)urlStr
    {
        NSURL *url = [NSURL URLWithString:urlStr];
        NSData *data = [NSData dataWithContentsOfURL:url]; // 这行会比较耗时
        return [UIImage imageWithData:data];
    }

    - (void)delay
    {
        //    NSLog(@"----touchesBegan----%@", [NSThread currentThread]);
        
        //    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //        [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
        //    });
        // 1.全局并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        // 2.计算任务执行的时间
        dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
        
        // 3.会在when这个时间点, 执行queue中的任务
        dispatch_after(when, queue, ^{
            NSLog(@"----run----%@", [NSThread currentThread]);
        });
    }
    //- (void)run
    //{
    //    NSLog(@"----run----%@", [NSThread currentThread]);
    //}

    三、NSOperation

      1.NSOperation基本使用

        - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        [self operationQueue2];
    }

    - (void)operationQueue2
    {
        NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"NSBlockOperation------下载图片1---%@", [NSThread currentThread]);
        }];
        NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"NSBlockOperation------下载图片2---%@", [NSThread currentThread]);
        }];
        [operation2 addExecutionBlock:^{
            NSLog(@"NSBlockOperation------下载图片22---%@", [NSThread currentThread]);
        }];
        
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        queue.maxConcurrentOperationCount = 2;
        
        [queue addOperation:operation1];
        [queue addOperation:operation2];
        
        [queue addOperationWithBlock:^{
            NSLog(@"NSBlockOperation------下载图片3---%@", [NSThread currentThread]);
        }];
    }

    - (void)opeationListen
    {
        NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0; i<10; i++) {
                NSLog(@"NSBlockOperation------下载图片---%@", [NSThread currentThread]);
            }
        }];
        operation.completionBlock = ^{
            // ...下载完图片后想做事情
            NSLog(@"NSBlockOperation------下载图片完毕---%@", [NSThread currentThread]);
        };
        
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        [queue addOperation:operation];
    }

    - (void)operationQueue
    {
        // 1.封装操作
        NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
    //    operation1.queuePriority = NSOperationQueuePriorityHigh
        
        NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
        
        NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0; i<10; i++) {
                NSLog(@"NSBlockOperation------下载图片---%@", [NSThread currentThread]);
                [NSThread sleepForTimeInterval:0.1];
            }
        }];
    //    [operation3 addExecutionBlock:^{
    //        for (int i = 0; i<10; i++) {
    //            NSLog(@"NSBlockOperation------下载图片2---%@", [NSThread currentThread]);
    //            [NSThread sleepForTimeInterval:0.1];
    //        }
    //    }];
        
        // 2.创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        queue.maxConcurrentOperationCount = 2; // 2 ~ 3为宜
        
        // 设置依赖
        [operation2 addDependency:operation3];
        [operation3 addDependency:operation1];
        
        // 3.添加操作到队列中(自动执行操作, 自动开启线程)
        [queue addOperation:operation1];
        [queue addOperation:operation2];
        [queue addOperation:operation3];
        
    //    [queue setSuspended:YES];
    }

    - (void)blockOperation
    {
        // 1.封装操作
    //    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    //        NSLog(@"NSBlockOperation------下载图片1---%@", [NSThread currentThread]);
    //    }];
        
        NSBlockOperation *operation = [[NSBlockOperation alloc] init];
        
        [operation addExecutionBlock:^{
            NSLog(@"NSBlockOperation------下载图片1---%@", [NSThread currentThread]);
        }];
        
        [operation addExecutionBlock:^{
            NSLog(@"NSBlockOperation------下载图片2---%@", [NSThread currentThread]);
        }];
        
        [operation addExecutionBlock:^{
            NSLog(@"NSBlockOperation------下载图片3---%@", [NSThread currentThread]);
        }];
        
        // 2.执行操作
        [operation start];
    }

    - (void)invocationOperation
    {
        // 1.创建操作对象, 封装要执行的任务
        NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
        
        // 2.执行操作(默认情况下, 如果操作没有放到队列queue中, 都是同步执行)
        [operation start];
    }

    - (void)download
    {
        for (int i = 0; i<10; i++) {
            NSLog(@"------download---%@", [NSThread currentThread]);
            [NSThread sleepForTimeInterval:0.1];
        }
    }

    - (void)run
    {
        for (int i = 0; i<10; i++) {
            NSLog(@"------run---%@", [NSThread currentThread]);
            [NSThread sleepForTimeInterval:0.1];
        }
    }

      NSInvocationOperation
      NSBlockOperation

     2.自定义NSOpertion

      

  • 相关阅读:
    关掉firefox(火狐)和palemoon地址栏自动加www.前缀功能【转】
    Maven入门指南(一)
    Linux 常用命令
    maven-编译速度优化
    monkeyscript
    GIT
    maven简介及基础使用
    使用xcrun打包iOS应用
    Mac系统下STF的环境搭建和运行
    npm介绍与cnpm介绍
  • 原文地址:https://www.cnblogs.com/syyjay/p/4276739.html
Copyright © 2011-2022 走看看