多线程(一)

面试题

1.你理解的多线程?
2.ios的多线程方案有哪几种?你更倾向于哪一种?
3.你在项目中用过GCD?
4.GCD的队列类型
5.说一下OperationQueue和GCD的区别、以及各自的优势
6.线程安全的处理手段有哪些?
7.OC你了解的锁有哪些?
    - 自旋和互斥对比?
    - 使用以上锁需要注意哪些?
  - 用C/OC/C++,任选其一,实现自旋或互斥?

下面我们来看看几个例子

代码详见 gitHub_Demo

例1

#import "ViewController_1.h"
@interface ViewController_1 ()
@end

@implementation ViewController_1
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //这句代码的本质是往Runloop中添加定时器  如果在主线程,runloop自动开启好了的
    [self performSelector:@selector(test1) withObject:nil afterDelay:.0];//afterDelay-->异步执行的 主队列
    //主队列异步执行时,会先执行完主线程上的代码,然后在主线程上顺序执行任务,不会有新的线程产生,所有任务都是在主线程上完成的
    
    NSLog(@"%@",[NSThread currentThread]);
    
    sleep(1);
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"asyncThread--%@",[NSThread currentThread]);
        NSLog(@"1");
        
        //如果是子线程需要自己手动去启动runloop
        [self performSelector:@selector(test) withObject:nil afterDelay:.0];
        
        //手动去启动runloop
//        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        
        NSLog(@"3");
    });
    NSLog(@"4");
}

-(void)test{
    NSLog(@"2");
}

-(void)test1{
    NSLog(@"test1Thread---%@",[NSThread currentThread]);
    NSLog(@"test1");
}
@end

打印:
没有手动去启动runloop
Multithreading[17114:917201] <NSThread: 0x600000501400>{number = 1, name = main}
Multithreading[17114:917201] 4
Multithreading[17114:917201] test1Thread---<NSThread: 0x600000501400>{number = 1, name = main}
Multithreading[17114:917201] test1
Multithreading[17114:917255] asyncThread--<NSThread: 0x6000005825c0>{number = 3, name = (null)}
Multithreading[17114:917255] 1
Multithreading[17114:917255] 3

=========================================
手动去启动runloop
Multithreading[17130:918365] <NSThread: 0x600000ef1400>{number = 1, name = main}
Multithreading[17130:918365] 4
Multithreading[17130:918365] test1Thread---<NSThread: 0x600000ef1400>{number = 1, name = main}
Multithreading[17130:918365] test1
Multithreading[17130:918422] asyncThread--<NSThread: 0x600000e712c0>{number = 3, name = (null)}
Multithreading[17130:918422] 1
Multithreading[17130:918422] 2
Multithreading[17130:918422] 3

例2

- (void)test{
    NSLog(@"2");
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"1"); //一完成任务之后,子线程就退出了
    }];
    [thread start];
    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
    NSLog(@"===End====");
}

运行结果:NSLog(@"1") 之后 崩溃了
Multithreading_01.png
- (void)test{
    NSLog(@"2");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"1");        
    //一完成任务之后,子线程就退出了
    }];
    
    [thread start];
    
    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:NO];
    
    sleep(2);
    NSLog(@"===End====");
}
打印:
Multithreading[19178:1044135] 1
Multithreading[19178:1043871] ===End====
Multithreading_02.png
修改崩溃:
- (void)test{
    NSLog(@"2");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"1");
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }];
    
    [thread start];
    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
    
    sleep(2);
    NSLog(@"===End====");
}
打印:
Multithreading[19214:1046668] 1
Multithreading[19214:1046668] 2
Multithreading[19214:1046548] ===End====

【开启了runloop后,延长了子线程的生命】
【如果没有,则会执行完任务子线程就退出了】

iOS中的常见多线程方案

Multithreading_03.png

GCD的常用函数

Multithreading_04.png

GCD的队列

Multithreading_05.png
//异步开启子线程,执行任务
-(void)test1{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //全局并发队列
    dispatch_async(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        // <NSThread: 0x6000033da280>{number = 3, name = (null)}
    });
}


//同步,在当前线程执行任务
-(void)test2{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //全局并发队列
    dispatch_sync(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        // <NSThread: 0x600002442c80>{number = 1, name = main}
    });
}


//同步,在当前线程执行任务(并发无效 )
-(void)test3{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //全局并发队列
    dispatch_sync(queue, ^{
        for(int i=0;i<5;i++){
            NSLog(@"执行任务1:%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for(int i=0;i<5;i++){
            NSLog(@"执行任务2:%@",[NSThread currentThread]);
        }
    });
    
    /*
    执行任务1:<NSThread: 0x600002b66980>{number = 1, name = main}
    执行任务1:<NSThread: 0x600002b66980>{number = 1, name = main}
    执行任务1:<NSThread: 0x600002b66980>{number = 1, name = main}
    执行任务1:<NSThread: 0x600002b66980>{number = 1, name = main}
    执行任务1:<NSThread: 0x600002b66980>{number = 1, name = main}
    执行任务2:<NSThread: 0x600002b66980>{number = 1, name = main}
    执行任务2:<NSThread: 0x600002b66980>{number = 1, name = main}
    执行任务2:<NSThread: 0x600002b66980>{number = 1, name = main}
    执行任务2:<NSThread: 0x600002b66980>{number = 1, name = main}
    执行任务2:<NSThread: 0x600002b66980>{number = 1, name = main}
     */
}


//异步,开启新线程执行任务(并发)
-(void)test4{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //全局并发队列
    dispatch_async(queue, ^{
        for(int i=0;i<5;i++){
            NSLog(@"执行任务1:%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for(int i=0;i<5;i++){
            NSLog(@"执行任务2:%@",[NSThread currentThread]);
        }
    });
    
    /*
     执行任务1:<NSThread: 0x6000018ac740>{number = 3, name = (null)}
     执行任务2:<NSThread: 0x6000018ac4c0>{number = 4, name = (null)}
     执行任务1:<NSThread: 0x6000018ac740>{number = 3, name = (null)}
     执行任务2:<NSThread: 0x6000018ac4c0>{number = 4, name = (null)}
     执行任务2:<NSThread: 0x6000018ac4c0>{number = 4, name = (null)}
     执行任务1:<NSThread: 0x6000018ac740>{number = 3, name = (null)}
     执行任务1:<NSThread: 0x6000018ac740>{number = 3, name = (null)}
     执行任务2:<NSThread: 0x6000018ac4c0>{number = 4, name = (null)}
     执行任务1:<NSThread: 0x6000018ac740>{number = 3, name = (null)}
     执行任务2:<NSThread: 0x6000018ac4c0>{number = 4, name = (null)}
     */
}


//串行队列异步执行任务 (按顺序执行)
-(void)test5{
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); //串行队列
    dispatch_async(queue, ^{
        for(int i=0;i<5;i++){
            NSLog(@"执行任务1:%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for(int i=0;i<5;i++){
            NSLog(@"执行任务2:%@",[NSThread currentThread]);
        }
    });
    /*
     执行任务1:<NSThread: 0x600002de93c0>{number = 3, name = (null)}
     执行任务1:<NSThread: 0x600002de93c0>{number = 3, name = (null)}
     执行任务1:<NSThread: 0x600002de93c0>{number = 3, name = (null)}
     执行任务1:<NSThread: 0x600002de93c0>{number = 3, name = (null)}
     执行任务1:<NSThread: 0x600002de93c0>{number = 3, name = (null)}
     执行任务2:<NSThread: 0x600002de93c0>{number = 3, name = (null)}
     执行任务2:<NSThread: 0x600002de93c0>{number = 3, name = (null)}
     执行任务2:<NSThread: 0x600002de93c0>{number = 3, name = (null)}
     执行任务2:<NSThread: 0x600002de93c0>{number = 3, name = (null)}
     执行任务2:<NSThread: 0x600002de93c0>{number = 3, name = (null)}
     */
}


//在当前线程(此时是主线程),串行执行任务
-(void)test6{
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); //串行队列
    dispatch_sync(queue, ^{
        for(int i=0;i<5;i++){
            NSLog(@"执行任务1:%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for(int i=0;i<5;i++){
            NSLog(@"执行任务2:%@",[NSThread currentThread]);
        }
    });
    
    /*
     执行任务1:<NSThread: 0x600000c69400>{number = 1, name = main}
     执行任务1:<NSThread: 0x600000c69400>{number = 1, name = main}
     执行任务1:<NSThread: 0x600000c69400>{number = 1, name = main}
     执行任务1:<NSThread: 0x600000c69400>{number = 1, name = main}
     执行任务1:<NSThread: 0x600000c69400>{number = 1, name = main}
     执行任务2:<NSThread: 0x600000c69400>{number = 1, name = main}
     执行任务2:<NSThread: 0x600000c69400>{number = 1, name = main}
     执行任务2:<NSThread: 0x600000c69400>{number = 1, name = main}
     执行任务2:<NSThread: 0x600000c69400>{number = 1, name = main}
     执行任务2:<NSThread: 0x600000c69400>{number = 1, name = main}
     */
}


//主队列里面异步执行(此时没有开启新的线程)
-(void)test7{
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        for(int i=0;i<5;i++){
            NSLog(@"执行任务1:%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for(int i=0;i<5;i++){
            NSLog(@"执行任务2:%@",[NSThread currentThread]);
        }
    });
    /*
     执行任务1:<NSThread: 0x600003d7d400>{number = 1, name = main}
     执行任务1:<NSThread: 0x600003d7d400>{number = 1, name = main}
     执行任务1:<NSThread: 0x600003d7d400>{number = 1, name = main}
     执行任务1:<NSThread: 0x600003d7d400>{number = 1, name = main}
     执行任务1:<NSThread: 0x600003d7d400>{number = 1, name = main}
     执行任务2:<NSThread: 0x600003d7d400>{number = 1, name = main}
     执行任务2:<NSThread: 0x600003d7d400>{number = 1, name = main}
     执行任务2:<NSThread: 0x600003d7d400>{number = 1, name = main}
     执行任务2:<NSThread: 0x600003d7d400>{number = 1, name = main}
     执行任务2:<NSThread: 0x600003d7d400>{number = 1, name = main}
     */
}

容易混淆的术语

Multithreading_06.png
dispatch_sync和dispatch_async用来控制是否要开启新的线程
 
队列的类型,决定了任务的执行方式(并发、串行)
 1.并发队列
 2.串行队列
 3.主队列(也是一个串行队列)

注意:异步的它不一定 要开启新的线程(只是具备开启新的线程的能力)
主队列里面异步执行任务

只要是sync(同步),或者是在主队列里,他就是在当前线程里面执行任务,那它一定是串行执行任务

没有开启新的线程,那它肯定是串行 执行任务的

各种队列的执行效果

Multithreading_07.png

总结:

Multithreading_08.png

友情链接:

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

推荐阅读更多精彩内容

  • 首先了解单线程:一、单线程的应用,整个应用中只有一个顺序执行流,当执行流在执行某个耗时的操作,或者不能立即完成的任...
    蓝白七七阅读 574评论 1 8
  • iOS开发中经常要使用到多线程,在面试的时候也是经常问到,比较常见的面试题有下面这些: iOS的多线程方案有哪几种...
    雪山飞狐_91ae阅读 852评论 0 2
  • 由于前段时间,堂弟的询问,再加上自己也想重新整理一下知识结构,就梳理一下知识。一系列的文章,可能会很多,也可能会很...
    摄影师诺风阅读 675评论 5 15
  • 多线程涉及到的概念: 进程,线程,主线程,任务,队列,死锁,串行,并行,同步,异步,GCD,NSOperation...
    BadGirl_TONG阅读 208评论 0 1
  • 白天黑夜抽丝剥茧着身体,迎着朝阳夕霞舞动着白白骷髅,褪去伪装剩下的只是皮和肉 。——迷途人
    清修_d5a0阅读 196评论 0 0