iOS - 多线程之GCD

简介

GCD, Grand Central Dispatch, 可译为”强大的中枢调度器”, 是一种异步执行任务技术。基于libdispatch, 纯C语言实现。(环境:Mac OS X 10.6 +, iOS 4+)

为什么要用 GCD

  • GCD 可用于多核的并行运算,会自动利用更多的 CPU 内核
  • GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

基本概念

队列

Dispatch Queue是执行处理的等待队列。
执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。Dispatch Queue按照是否等待处理可以分为serial Dispatch Queue和Concurrent Dispatch Queue。

串行队列(serial Dispatch Queue)

每次只有一个任务被执行,任务一个接着一个地执行。

并行队列(Concurrent Dispatch Queue)

在同一时间可以有多个任务被执行。

同步 (Synchronous)

在当前线程中执行任务,不具备开启新线程的能力。提交的任务在执行完成后才会返回。同步函数: dispatch_sync()。

异步 (Asynchronous)

在新线程中执行任务,具备开启新线程的能力。提交的任务立刻返回,在后台队列中执行。异步函数: dispatch_async()。

GCD的使用步骤和组合方式

  1. 创建一个队列(串行队列或并发队列)
  2. 将任务追加到任务的等待队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)
串行队列 并行队列 主队列
同步执行 不开启新线程,串行执行任务 不开启新线程,串行执行任务 死锁
异步执行 开启新线程(1条),串行执行任务 开启新线程,并发执行任务 不开启新线程,串行执行任务
同步执行 +串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("com.jun.serial",DISPATCH_QUEUE_SERIAL);
    NSLog(@"线程信息%@",[NSThread currentThread]);
    dispatch_sync(serialQueue,^{
        NSLog(@"1-线程信息%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue,^{
        NSLog(@"2-线程信息%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue,^{
        NSLog(@"3-线程信息%@",[NSThread currentThread]);
    });

执行结果
2019-11-11 11:52:12.024148+0800 iosTest[11225:645870] 线程信息<NSThread: 0x6000012b3040>{number = 1, name = main}
2019-11-11 11:52:12.024324+0800 iosTest[11225:645870] 1-线程信息<NSThread: 0x6000012b3040>{number = 1, name = main}
2019-11-11 11:52:12.024410+0800 iosTest[11225:645870] 2-线程信息<NSThread: 0x6000012b3040>{number = 1, name = main}
2019-11-11 11:52:12.024497+0800 iosTest[11225:645870] 3-线程信息<NSThread: 0x6000012b3040>{number = 1, name = main}

同步执行 + 并行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.jun.concurrent",DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"线程信息%@",[NSThread currentThread]);
    dispatch_sync(concurrentQueue,^{
        NSLog(@"1-线程信息%@",[NSThread currentThread]);
    });
    dispatch_sync(concurrentQueue,^{
        NSLog(@"2-线程信息%@",[NSThread currentThread]);
    });
    dispatch_sync(concurrentQueue,^{
        NSLog(@"3-线程信息%@",[NSThread currentThread]);
    });

执行结果
2019-11-11 11:55:08.379352+0800 iosTest[11279:647739] 线程信息<NSThread: 0x600001968540>{number = 1, name = main}
2019-11-11 11:55:08.379479+0800 iosTest[11279:647739] 1-线程信息<NSThread: 0x600001968540>{number = 1, name = main}
2019-11-11 11:55:08.379599+0800 iosTest[11279:647739] 2-线程信息<NSThread: 0x600001968540>{number = 1, name = main}
2019-11-11 11:55:08.379695+0800 iosTest[11279:647739] 3-线程信息<NSThread: 0x600001968540>{number = 1, name = main}

同步执行 + 主队列

在主队列中执行同步操作会引起死锁。

异步执行 + 串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("com.jun.serial",DISPATCH_QUEUE_SERIAL);
    NSLog(@"线程信息%@",[NSThread currentThread]);
    dispatch_async(serialQueue,^{
        NSLog(@"1-线程信息%@",[NSThread currentThread]);
    });
    dispatch_async(serialQueue,^{
        NSLog(@"2-线程信息%@",[NSThread currentThread]);
    });
    dispatch_async(serialQueue,^{
        NSLog(@"3-线程信息%@",[NSThread currentThread]);
    });

执行结果
2019-11-11 11:56:33.786021+0800 iosTest[11307:648849] 线程信息<NSThread: 0x600001d2e940>{number = 1, name = main}
2019-11-11 11:56:33.786268+0800 iosTest[11307:648902] 1-线程信息<NSThread: 0x600001d20240>{number = 3, name = (null)}
2019-11-11 11:56:33.786353+0800 iosTest[11307:648902] 2-线程信息<NSThread: 0x600001d20240>{number = 3, name = (null)}
2019-11-11 11:56:33.786454+0800 iosTest[11307:648902] 3-线程信息<NSThread: 0x600001d20240>{number = 3, name = (null)}

异步执行 + 并行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.jun.concurrent",DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"线程信息%@",[NSThread currentThread]);
    dispatch_async(concurrentQueue,^{
        sleep(3);
        NSLog(@"1-线程信息%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue,^{
        sleep(1);
        NSLog(@"2-线程信息%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue,^{
        sleep(2);
        NSLog(@"3-线程信息%@",[NSThread currentThread]);
    });

执行结果
2019-11-11 11:58:06.329150+0800 iosTest[11338:650092] 线程信息<NSThread: 0x600001b78e00>{number = 1, name = main}
2019-11-11 11:58:07.333266+0800 iosTest[11338:650148] 2-线程信息<NSThread: 0x600001b00500>{number = 3, name = (null)}
2019-11-11 11:58:08.331958+0800 iosTest[11338:650150] 3-线程信息<NSThread: 0x600001b09540>{number = 4, name = (null)}
2019-11-11 11:58:09.332356+0800 iosTest[11338:650149] 1-线程信息<NSThread: 0x600001b001c0>{number = 5, name = (null)}

异步执行 + 主队列
    dispatch_async(dispatch_get_main_queue(),^{
        NSLog(@"更新UI");
    });

其他常用方法

dispatch_after
	dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延迟3秒执行");
    });

对于时间要求不严格的可以使用这个方法,因为3秒只是block追到到主队列中的时间。如果主队列中有大量操作需要处理,会延迟操作的执行时间。第一个参数是指定的dispatch_time_t类型。该值有dispatch_time或dispatch_walltime。前者通常用于相对时间,后者为绝对时间。如果对时间精度要求高建议使用NSTimer。

dispatch_group_xxx
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"操作1");
    });
    dispatch_group_async(group, queue, ^{
        sleep(3);
        NSLog(@"操作2");
    });
    dispatch_notify(group, queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"耗时操作完成,更新UI");
        });
    });

dispatch_notify用于前面group中加入的block执行完后通知执行处理结果。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"操作1");
    });
    dispatch_group_async(group, queue, ^{
        sleep(3);
        NSLog(@"操作2");
    });

    dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
    
    NSLog(@"操作1和操作2完成之后才会执行");

暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_group_enter(group);
    dispatch_async(queue,^{
        NSLog(@"操作1");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue,^{
        sleep(8);
        NSLog(@"操作2");
        dispatch_group_leave(group);
    });
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"end");

dispatch_group_enter、dispatch_group_leave组合等同于dispatch_group_async。

dispatch_once
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"只执行一次的代码");
    });

dispatch_once 保证 block 只会被执行一次,一般用于单例模式中初始化 static 的单例对象。

dispatch_barrier_xxx
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.jun.concurrent", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueue, ^{
        sleep(8);
        NSLog(@"操作1");
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"操作2");
    });
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"操作3");
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"操作4");
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"操作5");
    });

只有在操作1、操作2完成之后 才会执行后续的操作。barrier就像一个栅栏,把queue分为barrier之前和barrier之后。先执行barrier之前的,再执行barrier之中的,最后执行barrier之后的。

dispatch_apply
    dispatch_queue_t globarQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(3,globarQueue,^(size_t index) {
        NSLog(@"%zu",index);
    });
    NSLog(@"end");

打印结果
2019-11-11 13:45:18.772546+0800 iosTest[12458:705620] 0
2019-11-11 13:45:18.772548+0800 iosTest[12458:705681] 2
2019-11-11 13:45:18.772549+0800 iosTest[12458:705682] 1
2019-11-11 13:45:18.772683+0800 iosTest[12458:705620] end

开启多条线程,并发执行,相比于for循环在耗时操作中极大的提高效率和速度。

dispatch_semaphore_xxx

可以借助信号量控制同时进行任务的数量。

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.jun.concurrent",DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrentQueue,^{
        NSLog(@"操作1");
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue,^{
        sleep(10);
        NSLog(@"操作2");
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue,^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"操作3");
    });

dispatch_semaphore_create:创建一个semaphore并初始化信号的总量。
dispatch_semaphore_signal:发送一个信号,让信号总量加1。
dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。
semaphore 是持有计数的信号,计数为0时等待,计数为1或者大于1,调用dispatch_semaphore_wait减1不继续等待。

dispatch_block_XXX

dispatch_block_wait会阻塞当前线程,并等待前面的任务执行完毕。
dispatch_block_notify 不会阻塞当前线程,会在指定的 block 执行结束后将指定 block 插入到指定的 queue 中。

    dispatch_queue_t serialQueue = dispatch_queue_create("com.jun.serial",DISPATCH_QUEUE_SERIAL);
    dispatch_block_t block1 = dispatch_block_create(0, ^{
        NSLog(@"start block1");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"end block1");
    });

    dispatch_async(serialQueue, block1);
    long result = dispatch_wait(block1, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
    if (result == 0) {
        NSLog(@"success perform block1");
    } else {
        NSLog(@"time out");
    }
    
    dispatch_block_t block2 = dispatch_block_create(0, ^{
        NSLog(@"start block2");
        [NSThread sleepForTimeInterval:3];
        NSLog(@"end block2");
    });
    dispatch_async(serialQueue, block2);
    dispatch_block_wait(block1, DISPATCH_TIME_FOREVER);
    dispatch_block_notify(block1,queue1,block2);
    dispatch_block_cancel(block2);
dispatch_set_target_queue
    dispatch_queue_t targetQueue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_set_target_queue(targetQueue, globalQueue);
    
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_set_target_queue(queue1, targetQueue);
    dispatch_set_target_queue(queue2, targetQueue);
    dispatch_async(queue1, ^{
        NSLog(@"queue1 1");
    });
    dispatch_async(targetQueue, ^{
        NSLog(@"async");
    });
    dispatch_async(queue2, ^{
        NSLog(@"queue2 1");
    });
    dispatch_async(queue2, ^{
        NSLog(@"queue2 2");
    });
    dispatch_async(queue2, ^{
        NSLog(@"queue2 3");
    });
    dispatch_async(queue1, ^{
        NSLog(@"queue1 2");
    });
    dispatch_async(queue1, ^{
        sleep(1);
        NSLog(@"queue1 3");
    });

输出结果
2019-11-25 16:53:57.804485+0800 iosTest[1981:130563] queue1 1
2019-11-25 16:53:57.804573+0800 iosTest[1981:130563] queue1 2
2019-11-25 16:53:58.808788+0800 iosTest[1981:130563] queue1 3
2019-11-25 16:53:58.808959+0800 iosTest[1981:130563] async
2019-11-25 16:53:58.809040+0800 iosTest[1981:130563] queue2 1
2019-11-25 16:53:58.809106+0800 iosTest[1981:130563] queue2 2
2019-11-25 16:53:58.809206+0800 iosTest[1981:130563] queue2 3

dispatch_suspend和dispatch_resume

dispatch_suspend(queue)//暂停某个队列
dispatch_resume(queue)//恢复某个队列
这些函数不会影响到队列中已经执行的任务,队列暂停后,已经添加到队列中但还没有执行的任务不会执行,直到队列被恢复。

发布了38 篇原创文章 · 获赞 5 · 访问量 9069

猜你喜欢

转载自blog.csdn.net/zj382561388/article/details/103003673