[iOS笔记]GCD介绍和基本使用

2018-05-01  本文已影响0人  Seacen_Liu

GCD是什么


GCD的优势

GCD的两个核心

GCD使用的基本步骤


队列的类型与创建

// 创建函数
dispatch_queue_t dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr);

执行任务的方式

void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

队列和执行方法的组合

串行队列 + 同步执行 (毫无用处)

NSLog(@"串行队列同步执行");
for(int i = 0; i < 10; i++) {
    dispatch_sync(self.serialQueue, ^{
    [NSThread sleepForTimeInterval:1];
    NSLog(@"test1: %zd, %@", i, [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1];
        });
}
// 0 1 2 3 4 5 6 7 8 9
// <NSThread: 0x600000066b80>{number = 1, name = main}

串行队列 + 异步执行 (非常有用)

NSLog(@"串行队列异步执行");
for(int i = 0; i < 10; i++) {
    dispatch_async(self.serialQueue, ^{
    [NSThread sleepForTimeInterval:1];
    NSLog(@"test2 %zd, %@", i, [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1];
    });
}
// 0 1 2 3 4 5 6 7 8 9
// <NSThread: 0x6000002746c0>{number = 3, name = (null)}

并发队列 + 同步执行 (有用,但是很容易出错)

NSLog(@"并行队列同步执行");
for(int i = 0; i < 10; i++) {
    dispatch_sync(self.concurrentQueue, ^{
    [NSThread sleepForTimeInterval:1];
    NSLog(@"test3 %zd, %@", i, [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1];
    });
}
// 0 1 2 3 4 5 6 7 8 9
// <NSThread: 0x600000066b80>{number = 1, name = main}

并发队列 + 异步执行 (几乎没用)

NSLog(@"并行队列异步执行");
for(int i = 0; i < 10; i++) {
    dispatch_async(self.concurrentQueue, ^{
    [NSThread sleepForTimeInterval:1];
    NSLog(@"test4 %zd, %@", i, [NSThread currentThread]);
    [NSThread sleepForTimeInterval:1];
    });
}
// 1 0 2 3 4 5 6 7 8 9
// 线程 4 3 5 6 7 8 9 10 11 12
// 瞬间完成

同步和异步决定了是否开启新的线程
串行和并发决定了任务的执行方式


系统提供的五个队列:

void dispatch_get_main_queue(void)
void dispatch_get_global_queue(long identifier, unsigned long flags);

延迟操作 dispatch_after

void dispatch_after(dispatch_time_t when,
    dispatch_queue_t queue,
    dispatch_block_t block);

单次执行 dispatch_once

void dispatch_once(dispatch_once_t *predicate,
        DISPATCH_NOESCAPE dispatch_block_t block);
+ (instancetype)sharedManager {
    static XXManager *sharedXXManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedXXManager = [[PhotoManager alloc] init];
    });
    return sharedXXManager;
}

多次执行 dispatch_apply

// iterations  执行的次数
// queue       提交到的队列
// block       执行的任务
void dispatch_apply(size_t iterations, 
                    dispatch_queue_t queue, 
                    DISPATCH_NOESCAPE void (^block)(size_t));

栅栏 dispatch_barrier

void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

- (NSString*)someString {
    __block NSString *localSomeString;
    dispatch_sync(_syncQueue, ^{
        localSomeString = someString;
    });
    return localSomeString;
}

- (void)setSomeString:(NSString*)someString {
    dispatch_barrier_async(_syncQueue, ^{
        _someString = someString;
    });
}
// 读取操作可以并行,但是修改操作必须单独执行

调度组 dispatch_group

// 创建调度组
dispatch_group_t dispatch_group_create(void);

// 进入调度组
void dispatch_group_enter(dispatch_group_t group);
// 离开调度组
void dispatch_group_leave(dispatch_group_t group);
// 一进一出,这两个函数的执行次数要匹配,不然会有奇怪是Bug。

// 等待调度组(第二个参数是等待的时间)
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
// 通知调度组(是一个宏来的,三个参数:调度组、block的执行队列、block)
dispatch_notify(<#ibject#>, <#queue#>, <#notification_block#>)
//  下面有两种写法是等价的
dispatch_group_async(group, queue, ^{ 
});
// 等价于
dispatch_group_enter(group);
dispatch_async(queue, ^{
  dispatch_group_leave(group);
});

挂起队列 dispatch_suspend/ 恢复队列dispatch_resume

// 挂起队列
void dispatch_suspend(dispatch_object_t object);
// 恢复队列
void dispatch_resume(dispatch_object_t object);

信号量 dispatch_semaphore

信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,是可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量VI,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。

但是这段文字可能过于抽象了,一般我们用停车的例子来阐释信号量控制线程的过程。

以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。
停车场中的剩余车位个数就是信号亮数

在GCD中有三个函数是semaphore的操作, 分别是:

// 创建信号
dispatch_semaphore_t dispatch_semaphore_create(long value);

// 发信号
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

// 等待 (有信号可用的时候返回0)
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
// 通常可以这样写
if (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) == 0) {
    NSLog(@"完成");
}
- (void)dispatch_semaphore
{
    NSLog(@"开始");
    //创建一个信号量容器
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    //进入调度组
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"A开始 %@",[NSThread currentThread]);
        //模拟请求耗时
        sleep(2);
        NSLog(@"A完成 %@",[NSThread currentThread]);
        //事件完成 离开调度组
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"B开始 %@",[NSThread currentThread]);
        sleep(1);
        NSLog(@"B完成 %@",[NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"C开始 %@",[NSThread currentThread]);
        sleep(3);
        NSLog(@"C完成 %@",[NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    
    if (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) == 0) {
        NSLog(@"完成");
    }
   
}
// 开始
// A开始 <NSThread: 0x604000272700>{number = 3, name = (null)}
// A完成 <NSThread: 0x604000272700>{number = 3, name = (null)}
// B开始 <NSThread: 0x604000272700>{number = 3, name = (null)}
// B完成 <NSThread: 0x604000272700>{number = 3, name = (null)}
// C开始 <NSThread: 0x604000272700>{number = 3, name = (null)}
// C完成 <NSThread: 0x604000272700>{number = 3, name = (null)}
// XPC connection interrupted 在使用线程休眠的有时会打印出来
// 完成

// 如果不使用信号量 任务的执行执行顺序和完成顺序都会不一样 只是单纯的并发队列异步执行

本文只是本人学习时候的记录和基本用法的介绍,有差错欢迎各位指正,更多的内容可以看下面一些大神的详细内容

iOS多线程:『GCD』详尽总结
GCD 深入理解:第一部分
GCD 深入理解:第二部分

上一篇 下一篇

猜你喜欢

热点阅读