美文网首页
iOS开发之GCD多线程一

iOS开发之GCD多线程一

作者: LuckyBugGo | 来源:发表于2018-11-07 11:24 被阅读0次

1. 多线程的一些相关概念

1.1 进程和线程:操作系统学习笔记之进程与线程

1.2 同步和异步

同步任务:在执行任务的过程中,按照顺序依次执行。
异步任务:在执行任务的过程中,可以同时触发执行。

1.3 串行和并行

串行队列:队列中任务依次执行。
并行队列:队列中任务同时执行。

2. 4种多线程操作情况

分别用同步串行、同步并行、异步串行和异步并行来执行以下函数。

-(void)task:(int)index{
    for (int i = 0; i<2; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"任务%d————当前线程:%@", index, [NSThread currentThread]);
    }
}

2.1 同步执行串行任务队列

-(void)syncSerial{
    dispatch_queue_t serialQueue = dispatch_queue_create("LJ_SERIAL_QUEUE", DISPATCH_QUEUE_SERIAL);
    NSLog(@"开始同步串行任务————————当前线程:%@", [NSThread currentThread]);
    dispatch_sync(serialQueue, ^{
        [self task:1];
    });
    dispatch_sync(serialQueue, ^{
        [self task:2];
    });
    dispatch_sync(serialQueue, ^{
        [self task:2];
    });
    NSLog(@"结束同步串行任务————————当前线程:%@", [NSThread currentThread]);
}

输出日记

开始同步串行任务————————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务1————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务1————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}
结束同步串行任务————————当前线程:<NSThread: 0x600001ae2fc0>{number = 1, name = main}

在同步中执行串行任务队列,不会创建新的线程,而且等待前一个任务执行完成之后才会去执行下一个任务。

2.2 同步执行并行任务队列

-(void)syncConcurrent{
    dispatch_queue_t concurrentQueue = dispatch_queue_create("LJ_CONCURRENT_QUEUE", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"开始同步并行任务————————当前线程:%@", [NSThread currentThread]);
    dispatch_sync(concurrentQueue, ^{
        [self task:1];
    });
    dispatch_sync(concurrentQueue, ^{
        [self task:2];
    });
    dispatch_sync(concurrentQueue, ^{
        [self task:3];
    });
    NSLog(@"结束同步并行任务————————当前线程:%@", [NSThread currentThread]);
}
开始同步并行任务————————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务1————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务1————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务3————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
任务3————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}
结束同步并行任务————————当前线程:<NSThread: 0x6000016711c0>{number = 1, name = main}

和同步串行任务的输出结果一模一样。也就是在同步任务中,串行队列和并行队列的执行结果都是一样的:不会创建新的线程,任务依次执行完成才会去执行下一个任务。

2.3 异步执行串行任务队列

-(void)asyncSerial{
    dispatch_queue_t serialQueue = dispatch_queue_create("LJ_SERIAL_QUEUE", DISPATCH_QUEUE_SERIAL);
    NSLog(@"开始异步串行任务————————当前线程:%@", [NSThread currentThread]);
    dispatch_async(serialQueue, ^{
        [self task:1];
    });
    dispatch_async(serialQueue, ^{
        [self task:2];
    });
    dispatch_async(serialQueue, ^{
        [self task:2];
    });
    NSLog(@"结束异步串行任务————————当前线程:%@", [NSThread currentThread]);
}
开始异步串行任务————————当前线程:<NSThread: 0x600003859400>{number = 1, name = main}
结束异步串行任务————————当前线程:<NSThread: 0x600003859400>{number = 1, name = main}
任务1————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}
任务1————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}
任务2————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}
任务2————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}
任务2————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}
任务2————当前线程:<NSThread: 0x60000380fec0>{number = 3, name = (null)}

在异步执行串行任务时,会新建一个线程出来专门执行这个队列的任务。这些任务也是依次执行完成之后才能执行下一个任务。

2.4 异步执行并行任务队列

-(void)asyncConcurrent{
    dispatch_queue_t concurrentQueue = dispatch_queue_create("LJ_CONCURRENT_QUEUE", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"开始异步并行任务————————当前线程:%@", [NSThread currentThread]);
    dispatch_async(concurrentQueue, ^{
        [self task:1];
    });
    dispatch_async(concurrentQueue, ^{
        [self task:2];
    });
    dispatch_async(concurrentQueue, ^{
        [self task:3];
    });
    NSLog(@"结束异步并行任务————————当前线程:%@", [NSThread currentThread]);
}
开始异步并行任务————————当前线程:<NSThread: 0x600001182940>{number = 1, name = main}
结束异步并行任务————————当前线程:<NSThread: 0x600001182940>{number = 1, name = main}
任务2————当前线程:<NSThread: 0x6000011ec080>{number = 3, name = (null)}
任务1————当前线程:<NSThread: 0x6000011cfc80>{number = 4, name = (null)}
任务3————当前线程:<NSThread: 0x6000011e0780>{number = 5, name = (null)}
任务2————当前线程:<NSThread: 0x6000011ec080>{number = 3, name = (null)}
任务3————当前线程:<NSThread: 0x6000011e0780>{number = 5, name = (null)}
任务1————当前线程:<NSThread: 0x6000011cfc80>{number = 4, name = (null)}

在异步执行并行任务时,不同的任务会创建不同的线程来执行,而且每个任务的执行都是并发的。

在执行异步任务时,不管并行还是串行任务,都会创建新的线程。而串行是只为整个任务队列创建一个线程,而并行则会为队列里面的每个任务都新建一个线程。

2.5 总结

在同步执行中,不管是串行队列还是并行队列,其行为都是一样的:都不会创建新的线程,任务都是依次完成在执行下一个。
在异步执行中,会为整个串行任务队列创建一个线程;并依次执行完任务、在并行任务队列中,会为每个任务都创建一个新的线程,并随机开始执行。
在iOS中,同步和异步的区别在于:是否基于原来的线程执行任务队列。同步是基于,而异步是新建一个线程。串行和并行的区别也在于:是否为每个人任务都新建一个线程来执行。串行是不会的,而并行是会为队列中的每个任务都新建一个线程。基于此,iOS开发中,并发编程的基础还是基于多线程本身的。

3. 主队列和主线程

每个APP都有且仅有一个主线程和一个主队列,它们随着APP的启动而创建,关闭而销毁。主队列是一个串行队列,它只能在主线程中执行。
获取主队列的方法:dispatch_get_main_queue
判读当前线程是否是主线程:NSThread.isMainThread。isMainThread是一个类属性。
因为主队列只能在主线程中执行,所以就算使用异步执行主队列中的任务,也不会创建新的线程。如以下示例:

    if (NSThread.isMainThread) {
        NSLog(@"当前线程为主线程:%@", [NSThread currentThread]);
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"主队列异步任务:%@", [NSThread currentThread]);
        });
    }
当前线程为主线程:<NSThread: 0x600001361400>{number = 1, name = main}
主队列异步任务:<NSThread: 0x600001361400>{number = 1, name = main}

一般来说与APP界面更新有关和ViewController生命周期相关的都是在主线程中执行的。

不能在主线程中同步执行主队列中的任务,会引发线程死锁效应。

    if (NSThread.isMainThread) {
        NSLog(@"当前线程为主线程:%@", [NSThread currentThread]);
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"主队列异步任务:%@", [NSThread currentThread]);
        });
    }
当前线程为主线程:<NSThread: 0x60000002ac00>{number = 1, name = main}
(lldb) 

以上,程序运行崩溃。

4. 全局并发队列

除了主队列之外,苹果官方还为我们提供了全局并发队列,它们是所有APP共用的队列。一般情况下,使用系统提供的全局并发队列就够了,不用再去创建新的并发队列。
dispatch_get_global_queue(long identifier, unsigned long flags);
identifier参数为队列的优先级,flags参数为标识,默认传0,如果传入其它值,可能会引发错误。
并发队列的优先级

// 最后优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
// 默认等级
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
// 低等级
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
// 后台等级
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

相关文章

网友评论

      本文标题:iOS开发之GCD多线程一

      本文链接:https://www.haomeiwen.com/subject/bgmfxqtx.html