iOS开发中的多线程问题

一、基本概念

在分析问题之前,我们先区分两个概念:进程和线程
进程:一个正在运行的程序可以看成是一个进程。 例如正在运行的QQ,进程拥有独立运行所需要的全部资源。
线程:程序中独立运行的代码段是线程。例如:接收QQ消息的代码段。
一个进程是由一个线程或者多个线程组成的,进程负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行。
我们知道,在ios里面 会自动为UI创建一个主线程,默认主线程分配的内存空间是 1MB。我们新开辟的子线程默认是512k,分配的大小必须是4的倍数,分配给子线程的空间是用来存储变量的,所以512k是充分够用的。
iOS运行的时候 程序在主入口自动添加自动释放池 而我们自己创建的释放池是没有自动释放池的 所以要我们手动添加,添加释放吃的原因是子线程共用堆内存 如果不及时回收随着子线程数量的上升 以及大量使用便利构造器 会对系统本身造成很大的负担。

了解以上之后,我们来定义一个方法 求0-63000000的和

-(void)test{
    NSLog(@"%@,%d",[NSThread currentThread],[[NSThread mainThread]isMainThread]);
    @autoreleasepool {
        long int sum = 0;
        long int num = 63550000;
        for (long int i = 1; i <= num; i++) {
            sum +=i;
        }
        NSLog(@"%ld",sum);
 }

调用以上方法我们发现返回计算结果的时间我们还能接受 但是在63000000后面加上一个或者更多0 再次试一次 就会发现 这个时间计算机的计算速度非常慢,这时候我们就可以考虑开辟多条线程来加快程序运行效率。

二、开辟多线程有哪些方式?

1、在后台执行 开辟多线程最简单的一个方法

    [self performSelectorInBackground:@selector(test) withObject:nil];

2、NSThread
NSThread是一个轻量级的线程 创建方式有两种:

/* 第一种*/
    //1 由init来创建并且需要手动来开启
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(test) object:nil];
    //2 开启线程
    [thread start];
    //3 取消线程
    [thread cancel];//这个很少有人在用 一般也不会写在这个地方
/* 第二种 */
    //初始化的时候自动开启线程
    [NSThread detachNewThreadSelector:@selector(test) toTarget:self withObject:nil];

3、NSOperation
NSOperation是一个抽象类 他有两个子类NSInvocationOperation 和NSBlockOperation 在MVC里面属于M层 封装了单个任务的数据和相关的代码 一般我们使用它的子类

    NSInvocationOperation *incocation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test) object:nil];
    __weak typeof(self)temp = self;
    NSBlockOperation *block =[NSBlockOperation blockOperationWithBlock:^{
        [temp test];
        NSLog(@"这个是BlockOprationQueue执行的");
    }];
//NSOperationQueue是线程操作队列 用来管理一组Operation对象 会根据实际需求创建出合适数量的子线程 完成任务的并发执行
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];

    //当最大并发执行数为1的时候程序是串行执行的
    [queue setMaxConcurrentOperationCount:1];

    //设置依赖关系[A addDependency B]若A依赖B则B先执行A在执行 若B依赖A则A先执行B在执行
    [invocation addDependency:blockOperation];

    #pragma mark ---上面两部要写在添加之前---
    [queue addOperation:invocation];
    [queue addOperation:blockOperation];

我们运行一下程序结果:



由于我们做了[invocation addDependency:blockOperation];只有当blockOperation执行完成之后,invocation才能执行。结果中的number数量就是线程个数 ,0表示非主线程,1则相反。很显然 我们开辟子线程了

4、GCD
GCD 三种队列:1 、 主队列 2、 全局队列 3 、 自定义队列(串行队列还是并行队列)
(1)主队列:获取朱队列生成一个串行的对列 队列里面的Block快按照先进先出的顺序执行实际上就是一个单线程队列

    dispatch_queue_t queue = dispatch_get_main_queue();
  //  网队列里面添加任务
    dispatch_async(queue, ^{
        [temp test];
        NSLog(@"1");
    });
    dispatch_async(queue, ^{
        [temp test];
        NSLog(@"2");
    });
    dispatch_async(queue, ^{
        [temp test];
        NSLog(@"3");
    });

结果显示:



当然根据需要我们也可以设置程序的延迟执行

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"今晚在如家402一起敲代码,好么?");
    });

(2)全局队列 并发执行

    dispatch_queue_t global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(global, ^{
        [temp test];
        NSLog(@"11");
    });
    dispatch_async(global, ^{
        [temp test];
        NSLog(@"22");
    });
    dispatch_async(global, ^{
        [temp test];NSLog(@"33");
    });

重复执行问题
重复执行的任务第一个参数size_t iterations是重复执行的次数dispatch_queue_t queue 是指定的队列

    dispatch_apply(5, global, ^(size_t t) {//相当于int t
        NSLog(@"执行到第%ld次",t);
    });

(3)自定义队列

    //串行队列
    dispatch_queue_t queue = dispatch_queue_create("AAA", DISPATCH_QUEUE_SERIAL);
        dispatch_async(queue, ^{
            [temp test];
            NSLog(@"111");
        });
        dispatch_async(queue, ^{
            [temp test];
            NSLog(@"222");
        });
        dispatch_async(queue, ^{
            [temp test];
            NSLog(@"333");
        });
    //分组任务  返回结果的先后顺序就不是确定的了 
    dispatch_group_t group  = dispatch_group_create();

        dispatch_group_async(group, queue, ^{
            [temp test];
            NSLog(@"1111");
        });
        dispatch_group_async(group, queue, ^{
            [temp test];
            NSLog(@"2222");
        });
        dispatch_group_async(group, queue, ^{
            [temp test];
            NSLog(@"3333");
        });
        //这句话就是一个谦虚的狗做的 拒绝拿第一
        dispatch_group_notify(group, queue, ^{
            NSLog(@"昭哥变成了数学家");
        });
    //障碍执行 执行任务的时候 要是前面还有任务的话就会等其他任务执行完之后在再执行 
该方法创建的任务在执行的时候 会先检查是不是有其他任务正在执行
要是有等待其执行完毕 再进行执行 否则其他任务要等待其执行完毕之后再执行
    dispatch_barrier_sync(queue, ^{
        [temp test];
        NSLog(@"这个叫做障碍执行");
    });

//不争不抢 最后一步执行
    dispatch_group_notify(queue3, queue2, ^{
        NSLog(@"昭哥变成了数学家");
    });

三、子线程回到主线程

简单介绍几种常用子线程回到主线程的方式

//    GCD
    dispatch_async(dispatch_get_main_queue(), ^{
        //在这里面进行主线程任务的操作(UI的刷新)
    });
//    NSObject
//    (1)
    [self performSelectorOnMainThread:@selector(backMainThread) withObject:nil waitUntilDone:YES];
//    (2)
    [self performSelector:@selector(backMainThread) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES];

4、模拟购票系统

声明一个全局变量 int count = 10000; 假设10000张票
声明锁 @property(nonatomic,strong)NSLock *lock;并进行初始化

__weak typeof(self)temp = self;
    dispatch_queue_t queue3 = dispatch_queue_create("CCC", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue3, ^{
        for (int i = 0; i < 100; i++) {
            [temp buyTickets];
        }
    });
    dispatch_async(queue3, ^{
        for (int i = 0; i < 100; i++) {
            [temp buyTickets];
        }
    })
防止发生购票冲突 我们使用了互斥锁
-(void)buyTickets{
    //线程互斥
    @autoreleasepool {
        [_lock lock];
        count--;
        NSLog(@"剩余票数%d",count);
        [_lock unlock];
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容