GCD的知识整理

参考:https://juejin.im/post/5a90de68f265da4e9b592b40

基础API

// 系统标准提供的两个队列
// 全局队列,也是一个并行队列  
dispatch_get_global_queue
// 主队列,在主线程中运行,因为主线程只有一个,所以这是一个串行队列  
dispatch_get_main_queue
// 除此之外,还可以自己生成队列
// 串行队列(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)
dispatch_queue_create("队列名", DISPATCH_QUEUE_SERIAL)
// 并行队列(虽然并发队列可以开启多个线程,并且同时执行多个任务。但是因为本身不能创建新线程)
dispatch_queue_create("队列名", DISPATCH_QUEUE_CONCURRENT)
// 接下来是同步与异步线程的创建:
dispatch_sync(..., ^(block)) // 同步线程
dispatch_async(..., ^(block)) // 异步线程
//串行队列+同步线程不会开启新线程

1.串行队列:任务按顺序执行,一个执行完毕执行下一个任务,无论同步任务或是异步任务,因为串行队列 执行同步任务不开辟线程,执行异步任务开辟最多开辟一条线程,所以必须顺序执行。
2.并行队列的特点,执行异步任务具备开辟多条线程的能力,执行同步任务,顺序执行。因为没有开辟线程。
3.开不开线程,取决的任务,同步不开新线程,异步开新线程。
4.开几条线程取决于队列,串行队列开一条线程,并行队列在执行多个异步任务时会开辟多条线程。

dispatch_group 队列组

分别异步执行2个耗时任务,然后当2个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组。

    //第一步创建队列
    dispatch_queue_t customQuue = dispatch_queue_create("队列名称", DISPATCH_QUEUE_CONCURRENT);
     //第二步创建组
    dispatch_group_t customGroup = dispatch_group_create();
    //第三步添加任务
    dispatch_group_async(customGroup, customQuue, ^{
        NSLog(@"吃饭");
    });
    dispatch_group_async(customGroup, customQuue, ^{
        NSLog(@"洗澡");
    });
    //第四步通知
    dispatch_group_notify(customGroup, dispatch_get_main_queue(), ^{
        NSLog(@"任务完成");
    });

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


   //这里的dispatch_group_enter、dispatch_group_leave组合,其实等同于dispatch_group_async。
    dispatch_group_enter(customGroup);
    dispatch_async(customQuue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
        dispatch_group_leave(group);
    });

dispatch_semaphore 信号量

GCD 中的信号量是指 Dispatch Semaphore,是持有计数的信号。类似于过高速路收费站的栏杆。可以通过时,打开栏杆,不可以通过时,关闭栏杆。在 Dispatch Semaphore 中,使用计数来完成这个功能,计数为0时等待,不可通过。计数为1或大于1时,计数减1且不等待,可通过。
Dispatch Semaphore 提供了三个函数。

1.dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量
2.dispatch_semaphore_signal:发送一个信号,让信号总量加1
3.dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。

注意:信号量的使用前提是:想清楚你需要处理哪个线程等待(阻塞),又要哪个线程继续执行,然后使用信号量。
Dispatch Semaphore 在实际开发中主要用于:
保持线程同步,将异步执行任务转换为同步执行任务
保证线程安全,为线程加锁

上代码

/**
 * 线程安全:使用 semaphore 加锁
 * 初始化火车票数量、卖票窗口(线程安全)、并开始卖票
 */
- (void)initTicketStatusSave {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"semaphore---begin");
    
    semaphoreLock = dispatch_semaphore_create(1);
    
    self.ticketSurplusCount = 50;
    // queue1 代表北京火车票售卖窗口
    dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
    // queue2 代表上海火车票售卖窗口
    dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
    
    __weak typeof(self) weakSelf = self;
    dispatch_async(queue1, ^{
        [weakSelf saleTicketSafe];
    });
    dispatch_async(queue2, ^{
        [weakSelf saleTicketSafe];
    });
}

/**
 * 售卖火车票(线程安全)
 */
- (void)saleTicketSafe {
    while (1) {
        // 相当于加锁
        dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
        if (self.ticketSurplusCount > 0) {  //如果还有票,继续售卖
            self.ticketSurplusCount--;
            NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        } else { //如果已卖完,关闭售票窗口
            NSLog(@"所有火车票均已售完");
            // 相当于解锁
            dispatch_semaphore_signal(semaphoreLock);
            break;
        }
        // 相当于解锁
        dispatch_semaphore_signal(semaphoreLock);
    }
}
输出结果为:
2018-02-23 22:32:19.814232+0800 YSC-GCD-demo[20862:5290531] currentThread---{number = 1, name = main}
2018-02-23 22:32:19.814412+0800 YSC-GCD-demo[20862:5290531] semaphore---begin
2018-02-23 22:32:19.814837+0800 YSC-GCD-demo[20862:5290687] 剩余票数:49 窗口:{number = 3, name = (null)}
2018-02-23 22:32:20.017745+0800 YSC-GCD-demo[20862:5290689] 剩余票数:48 窗口:{number = 4, name = (null)}
2018-02-23 22:32:20.222039+0800 YSC-GCD-demo[20862:5290687] 剩余票数:47 窗口:{number = 3, name = (null)}
......
2018-02-23 22:32:29.024817+0800 YSC-GCD-demo[20862:5290689] 剩余票数:4 窗口:{number = 4, name = (null)}
2018-02-23 22:32:29.230110+0800 YSC-GCD-demo[20862:5290687] 剩余票数:3 窗口:{number = 3, name = (null)}
2018-02-23 22:32:29.433615+0800 YSC-GCD-demo[20862:5290689] 剩余票数:2 窗口:{number = 4, name = (null)}
2018-02-23 22:32:29.637572+0800 YSC-GCD-demo[20862:5290687] 剩余票数:1 窗口:{number = 3, name = (null)}
2018-02-23 22:32:29.840234+0800 YSC-GCD-demo[20862:5290689] 剩余票数:0 窗口:{number = 4, name = (null)}
2018-02-23 22:32:30.044960+0800 YSC-GCD-demo[20862:5290687] 所有火车票均已售完
2018-02-23 22:32:30.045260+0800 YSC-GCD-demo[20862:5290689] 所有火车票均已售完

栅栏函数dispatch_barrier_async

dispatch_barrier_async函数,在进程管理中起到一个栅栏的作用,它等待所有位于barrier函数之前的操作执行完毕后执行,并且在barrier函数执行之后,barrier函数之后的操作才会得到执行,该函数需要同dispatch_queue_create函数生成的DISPATCH_QUEUE_CONCURRENT队列一起使用。
栅栏函数的这个特点,使得它非常适合用于做多读单写读写锁。比如说对于一个数据,可以多线程读取,但是只能单线程修改,就非常适合用栅栏函数dispatch_barrier和同步函数dispatch_sync配合并行队列做数据的读写安全机制。

栅栏函数使用注意事项

为什么不能跟全局并行队列配合使用呢?
原因在于全局队列属于系统创建并管理,这个队列不止我们app在用,系统也在用。里面很多涉及到系统自身相关的操作,一旦我们外部app阻塞这个队列,有可能会影响系统相关的操作。因此栅栏函数对全局(globa)并行队列的操作是无效的 (dispatch_get_global_queue)

在
dispatch_queue_t  线程名字 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
中
dispatch_async  = dispatch_barrier_async

线程加锁

加锁方式一 dispatch_semaphore 自旋锁
加锁方式二 @synchronized 互斥锁 @synchronized(self) { ---}
加锁方式三 NSLock --- [_lock lock]; [_lock unlock];

swfit待更新

你可能感兴趣的:(GCD的知识整理)