• 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

      

  • 相关阅读:
    webbrowser获取页面文章指定段落内容
    webbrowser防止弹窗(IE)
    webbrowser模拟手动输入
    WPF加载Winform窗体时 报错:子控件不能为顶级窗体
    FAQs: 我们可以在那里来为我的没有提升管理权限的应用程序存储用户数据?
    Winform中修改WebBrowser控件User-Agent的方法(已经测试成功)
    必应代码搜索 Bing Code Search 安装
    Microsoft Visual Studio Professional 2012 专业版 下载
    vs2012 aspx 没有设计视图了?
    vs2010 Express 下载连接
  • 原文地址:https://www.cnblogs.com/syyjay/p/4276739.html
Copyright © 2020-2023  润新知