茶余饭后的多线程

茶余饭后的多线程

故事是这样的:

艳:戴戴,我最近要面试,但是我都不知道会面试什么东西!

戴:多线程,内存,源码,runtime等!

艳不爽:我也知道是这些,可是具体面试啥呢?

戴:比如多线程!额...

沉思片刻...

额 ....

艳:切。

戴:额... 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。

艳:能不能具体点!

戴:这样!假如我现在想做一个下载的功能,但是呢,我肯定不能在主线程上下载对不对?

嗯...

那我就需要开个子线程

异步下载

dispatch_queue_t queue = dispatch_queue_create("1", nil);
dispatch_async(queue, ^{
    NSLog(@"download...");
});

就像这样,我创建个线程让他异步执行就好了。

艳:但是这样下载完了,主线程根本不知道!

戴:可以这样

异步下载之后回调主线程

dispatch_queue_t queue1 = dispatch_queue_create("1", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue1, ^{
    //这里假设sleep是在download
    sleep(rand()%10);
    NSLog(@"download...");
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"dowload complete!");
    });
});

这里有一个要注意一下哈!

  • DISPATCH_QUEUE_CONCURRENT:并行队列,以先进先出的方式,并发调度队列中的任务执行
    如果当前调度的任务是同步执行的,会等待任务执行完成后,再调度后续的任务
    如果当前调度的任务是异步执行的,同时底层线程池有可用的线程资源,会再新的线程调度后续任务的执行

  • DISPATCH_QUEUE_SERIAL:串行队列,永远是任务一个一个的执行

艳:那我明白了,如果我想要同时下载5个文件我就可以这样

创建并行队列,多任务同步执行

for (NSInteger i = 0 ; i < 5; i ++ ) {
    dispatch_queue_t dqueue = dispatch_queue_create("1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(dqueue, ^{
        //这里假设sleep是在download
        sleep(rand()%10);
        NSLog(@"dowload %ld",i);
    });
}

戴:没错的,可是如果有100个,1000个文件要下载呢?

艳:那我就for循环100次,1000...好像不大对哦?

戴:因为设备性能的问题,100,1000循环创建,这种方式明显不可取,那也就是我们需要让他每次并行的数量有一个控制。这时候我们会联想到一个名词叫做信号量!

信号量:信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。

通过信号量控制,同时只有五个任务在下载

dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
dispatch_queue_t ddqueue = dispatch_queue_create("1", DISPATCH_QUEUE_CONCURRENT);

for (NSInteger i = 0; i < 100; i ++) {
    dispatch_async(ddqueue, ^{
        sleep(rand()%10);
        NSLog(@"dowload %ld",i);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }
}

这里我解释一下这个信号量怎么运作的。

  • dispatch_semaphore_create:创建信号量,并且传入的参数是个long类型,意义就是可以同时存在多少资源数,这里可以理解为线程
  • dispatch_semaphore_signal:发送信号,会使当前信号量 +1
  • dispatch_semaphore_wait:减低信号,如果当前信号量大于1,会使当前信号量-1,如果等于0 进入等待。后面跟的时间就是等待的时长了。

那么上面的代码意思就是我同时开始下载的任务只有五个,有一个下载完就会进入下一个了。

艳:嗯...我如果想给任务排个序,就是知道完成的顺序怎么办呢?

戴:这就涉及到多线程里面比较常用的锁了。

多线程中的锁

NSLock *lock = [[NSLock alloc] init];
__block NSInteger index = 0;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
dispatch_queue_t ddqueue = dispatch_queue_create("1", DISPATCH_QUEUE_CONCURRENT);

for (NSInteger i = 0; i < 100; i ++) {
    dispatch_async(ddqueue, ^{
        sleep(rand()%10);
        [lock lock];
        NSLog(@"the index of %ld: %ld",index,i);
        index ++;
        [lock unlock];
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}

这样就会有顺序了,对index进行读写的时候进行加锁,防止index被其他线程给改了。

艳:那么如果有些任务要是失败了,我是不是应该给他重新下,这个怎么做?

戴:有两种方案:

  • 方案1:如果不成功就重复调用当前线程,也就是如果不成功就重复跑下载线程,让他占着资源。
  • 方案2:如果不成功,就把当前任务丢到任务尾部,等其他任务下载完了之后再去执行这个任务。

大多下载工具的实现是 方案1 + 方案2 的结合。

为了简单,我就只展示给你看下方案2哈,因为方案一,跟多线程关系不大。

这里我用一个array来代表一些下载任务吧。

多任务下载的下载失败处理

NSInteger totalCount = 100;
NSLock *lock = [[NSLock alloc] init];
NSMutableArray *needDownloadarray = [[NSMutableArray alloc] init];
for (int i =0 ; i < totalCount; i ++) {
    [array addObject:@1];
}
__block NSInteger index = 0;
__block NSInteger completeCount = 0;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t ddqueue = dispatch_queue_create("1", DISPATCH_QUEUE_CONCURRENT);

BOOL stop = false;
while (completeCount != totalCount && !stop) {
    if (needDownloadarray.count > 0) {
        [lock lock];
        [needDownloadarray removeObject:[needDowanloadarray firstObject]];
        [lock unlock];
        dispatch_async(ddqueue, ^{
            NSInteger randomcount = rand()%10;
            sleep((unsigned int)randomcount / 5);
            [lock lock];
            index ++;
            if (randomcount > 8) {
                [needDownloadarray addObject:@1];
                NSLog(@"the index of %ld , download failed",index);
            } else {
                completeCount ++ ;
                NSLog(@"the index of %ld , complete is %ld",index,completeCount);
            }

            [lock unlock];
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }
}
NSLog(@"download completed!");

如果随机数大于8,我们假设他失败吧!哈哈!

  • 如果下载失败,就把任务丢到needDownloadarray最后面

戴:这下明白了不!

艳:嗯

戴:其实嘛,这种多个文件下载,直接打个包下载就好了,下下来解压一下就完了,文件太大给他搞个断点续传,就美滋滋了。

艳:那你不早说!

戴:你不是想知道多线程么?

艳:切!那你跟我说说断点续传!

戴:我地乖乖,不早了,洗洗睡吧!明天...明天...

注意

  • 信号量里面的wait,signal要配对使用,不然也会造成死锁。
  • 多线程访问同一块内存的时候,尽量加锁
  • 加锁实际上会增加调度消耗,并不是锁越多越好,怎么把锁的调度变得合理是一门课题。
  • 还有很多注意大家自己发掘
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • iOS多线程编程 基本知识 1. 进程(process) 进程是指在系统中正在运行的一个应用程序,就是一段程序的执...
    陵无山阅读 6,026评论 1 14
  • 本文用来介绍 iOS 多线程中 GCD 的相关知识以及使用方法。这大概是史上最详细、清晰的关于 GCD 的详细讲...
    花花世界的孤独行者阅读 497评论 0 1
  • 这颗最爱。人生坎坷,失双亲,工作受挫,原本以为活着沒有意义。成亲了,有了奔头,有了后代,存了希望。但婚变毁了一切,...
    秃瓜阅读 134评论 0 0
  • 我在家门口的北山访客中心做义工三年左右了。第一次到这个地方还是我和先生一起转山时不知不觉拐进去的,我们都惊讶于它的...
    凤城物语阅读 261评论 0 0
  • 多少相思 吹不熄多年奢侈的孤独 一种执念 越长大越不舍得轻易撒手 记忆中散碎的烟花 毫无预谋的在 甜蜜的岁月 清澈...
    风沙蚀骨阅读 1,550评论 96 206