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]);
//    });
    
    // 不会开启新的线程, 所有任务在主线程中执行
}

// 凡是函数名种带有create\copy\new\retain等字眼, 都需要在不需要使用这个数据的时候进行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]);
//}

  

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。