iOS开发笔记-多线程的使用方法

多线程方式一:GCD
队列的3个种类:

  1. 自建队列: 分并行/串行
  2. 全局队列: 属于并行队列, 是系统默认创建的.
  3. 主队列: 主线程上的队列. 串行队列

1.自建队列四种
异步:异于当前方法所在线程 async
同步:同于当前方法所在线程 sync
串行:任务按顺序执行 SERIAL
并行:任务没有顺序,同时执行 CONCURRENT

switch (indexPath.section) {
        case 0: //异步串行
        {
            //队列:任务是放在队列中的
            //SERIAL串行,序列  CONCURRENT 并行
            dispatch_queue_t queue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL);
            //队列放方法,并且规定方法在哪个线程中执行
            //把参数2的方法放到参数1的队列中,异步执行
            dispatch_async(queue, ^{
                [self doMethod0];
            });
            dispatch_async(queue, ^{
                [self doMethod1];
            });
        }
            break;
        case 1: //异步并行
        {
            dispatch_queue_t queue = dispatch_queue_create("并行", DISPATCH_QUEUE_CONCURRENT);
            dispatch_async(queue, ^{
                [self doMethod0];
                //[self doMethod1];这样写,自然就串行了
            });
            dispatch_async(queue, ^{
                [self doMethod1];
            });
        }
            break;
        case 2://同步串行
        {
            [self doMethod0];
            [self doMethod1];
            return;
            dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
            dispatch_sync(queue, ^{
                [self doMethod0];
            });
            dispatch_sync(queue, ^{
                [self doMethod1];
            });
        }
            break;
        case 3: //同步并行
        {
            dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
            dispatch_sync(queue, ^{
                [self doMethod0];
            });
            dispatch_sync(queue, ^{
                [self doMethod1];
            });
        }
            break;
            
        default:
            break;
    }

2.全局队列和主队类

- (void)viewDidLoad {
    [super viewDidLoad];
    //dispatch_get_global_queue获取全局队列, 参数1代表优先级, 0是默认优先级. 参数2 无用
   dispatch_async(dispatch_get_global_queue(0, 0), ^{
       for (int i = 0; i < 10000; i++) {
           NSLog(@"%@, %d", [NSThread currentThread], i);
       }
       //dispatch_get_main_queue() 主队列 嵌套在全局队列中 与上方for循环属于全局队列中的一个任务所以是串行的
       dispatch_async(dispatch_get_main_queue(), ^{
          //所有与UIKit相关的操作一定要放到主线程中
           UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
           v.backgroundColor = [UIColor redColor];
           [self.view addSubview:v];
           v.center = self.view.center;
       });
   });
}

barrier(墙) 和分组

- (void)refreshImage{
    NSArray *imagePaths = @[@"http://imgstore.cdn.sogou.com/app/a/11220002/7291_pc.jpg", @"http://b.zol-img.com.cn/desk/bizhi/image/3/960x600/1376532627798.jpg", @"http://pic27.nipic.com/20130304/11627225_191300344144_2.jpg"];
    //barrier(墙): 墙必须加到自建队列中.
    dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    [imagePaths enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSURL *url = [NSURL URLWithString:obj];
        dispatch_async(queue, ^{
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *img = [UIImage imageWithData:data];
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imageViews[idx].image = img;
            });
        });
    }];
    //在自建的并行队列中添加墙, 墙只有在并行队列中的所有任务都执行结束后, 才会被推倒
    dispatch_barrier_async(queue, ^{
        NSLog(@"%@", [NSThread currentThread]);
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    });
    
    return;
    //可以把多个子线程放到一个分组中, 通过监听此分组的情况来决定一些操作
    dispatch_group_t group = dispatch_group_create();
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    [imagePaths enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSURL *url = [NSURL URLWithString:obj];
        dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imageViews[idx].image = image;
            });
        });
    }];
    
    //监听分组中的线程都执行结束的时机
    dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@", [NSThread currentThread]);
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    });
    
    
}

死锁

- (void)viewDidLoad {
    [super viewDidLoad];
    /*
     死锁: 在`主线程` 中 调用 `dispatch_sync` 同步方法
     向 `主线程` 中放入一个任务
     
     dispatch_sync() will not return until the block has finished.
     dispatch_sync()这个方法直到block完成以后,才返回.
     
     下方的代码是 向主线程中插入一个 block. 根据官方要求 必须是这个block执行结束以后,dispatch_sync()方法才能执行完毕.
     线程特点: 一个线程同时只能做一件事情, 多个任务是排队执行的.
     看主线程队列: dispatch_sync()方法 -> 打印block方法
     dispatch_sync()的完成需要block方法执行完毕.
     因为排队问题 block 必须在 dispatch_sync() 执行完毕后再进行!!!
     */
   
    /*
     面试问题:下方方法是否有什么问题?
     答: 有可能造成死锁. 因为取决于这个方法执行的线程, 只有在主线程才会死锁.. 说下死锁的三个关键点.
     */
    
    //dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@".....");
        });
    //});
    
    //如果解决这个死锁问题?
    //dispatch_async()方法不要内部的block完成就可以完成
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@".....");
    });
    
    self.view.backgroundColor = [UIColor yellowColor];
    
    
}

多线程方式二:NSThread

- (void)redBtnClicked:sender{
    NSLog(@"redBtnClicked %@", [NSThread currentThread]);
    //在后台线程执行方法
    //[self performSelectorInBackground:@selector(wasteTime) withObject:nil];
    
    //方式2:
    //[NSThread detachNewThreadSelector:@selector(wasteTime) toTarget:self withObject:nil];
    
    //方式3:
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(wasteTime) object:nil];
    thread.name = @"1606";
    [thread start];//必须手动开启
    
}
//开启一个新的线程, 在线程中做耗时操作
- (void)wasteTime{
    //[NSThread currentThread]: 获取当前方法所在的线程
    //number=1 代表主线程, 其他都是子线程
    for (int i = 0; i < 10000; i++) {
        NSLog(@"%d , %@", i, [NSThread currentThread]);
    }

    //从子线程回归主线程,规避掉线程不安全的操作
    [self performSelectorOnMainThread:@selector(changeUI) withObject:nil waitUntilDone:NO];
    //waitUntilDone:表示是否要等待changeUI这个方法执行结束以后, 才继续执行下方代码
    NSLog(@"--------------------");
}

多线程方式三:NSOperation
NSOperation 就是 使用OC语法 对 GCD做的封装, 效率上略低(可以忽略不计), 继续如何选择 看个人喜好
NSOperationQueue : 队列
NSOperation : 任务
任务 存放在 队列 中, 队列 存放在 线程

//NSOperation 封装了GCD之后, 额外增加的易用方法
- (void)runMethod1{
    NSOperationQueue *queue = [NSOperationQueue new];
    
    NSBlockOperation *op0 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op0 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op0 结束, %@", [NSThread currentThread]);
    }];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op1 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op1 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op2 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op2 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op3 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op3 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op4 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op4 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op5 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op5 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op6 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op6 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op6 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op7 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op7 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op7 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op8 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op8 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op8 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op9 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op9 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op9 结束, %@", [NSThread currentThread]);
    }];
    //以为单线程CPU(硬件), 开启多线程, 是伪多线程. 就是把cpu的线程分很多个时间片, 每个时间片做一件事. 因为速度太快了, 所以显得跟多线程一样.
    //最佳的线程数量是3~5个
    
    //NSOperatin就提供了最大并发数量的属性, 此属性必须在任务添加到队列`之前`写才有用!!
    queue.maxConcurrentOperationCount = 3;
    
    //规定线程依赖. 比如a必须等b线程做完才能进行
    //op0 必须等 op7 和 op9 执行完毕 才能进行
    [op0 addDependency:op7];
    [op0 addDependency:op9];
    
    //任务添加到队列中之后, 任务就会自动执行
    //[queue addOperation:op0];
    //waitUntilFinished:参数表示,下方的打印方法是否要等待队列中的任务执行结束之后, 再运行
    [queue addOperations:@[op0, op1, op2, op3, op4, op5, op6, op7, op8, op9] waitUntilFinished:NO];
    NSLog(@"!@#$##$^^&^&$^@#$");
    
    
    //取消线程中的所有任务: 已经执行的任务无法取消
    //[queue cancelAllOperations];
    
    //取消个别任务
    [op2 cancel]; //已经执行的取消不了
    [op9 cancel]; //0依赖9, 9被取消, 0就不再依赖9
    
    //暂停, 已经运行的任务 无法暂停
    queue.suspended = YES;
    sleep(10);
    queue.suspended = NO;
}

//开子线程+回归主线程
- (void)runMethod0{
    //[NSOperationQueue new] 自动开启子线程
    [[NSOperationQueue new] addOperationWithBlock:^{
        NSLog(@"[NSOperationQueue new], %@", [NSThread currentThread]);
        //回归主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
           NSLog(@"[NSOperationQueue mainQueue], %@", [NSThread currentThread]);
        }];
    }];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • 进程和线程 首先,在了解多线程之前要了解什么是进程,什么是线程 什么是进程呢?进程是指在系统中正在运行的一个应用程...
    搁浅的青蛙阅读 368评论 0 0
  • 对于多线程的理解:多线程: 利用多核处理器的优势,实现多个操作的并发执行,使系统资源得到充分利用,提高运行效率,减...
    大猿媛阅读 273评论 0 0
  • 从哪说起呢? 单纯讲多线程编程真的不知道从哪下嘴。。 不如我直接引用一个最简单的问题,以这个作为切入点好了 在ma...
    Mr_Baymax阅读 2,757评论 1 17
  • 背景 担心了两周的我终于轮到去医院做胃镜检查了!去的时候我都想好了最坏的可能(胃癌),之前在网上查的症状都很相似。...
    Dely阅读 9,237评论 21 42
  • 前言 iOS多线程有四种:pthread(最古老的),NSThread,NSOperation,GCD 一、进程和...
    GitHubPorter阅读 357评论 0 3