GCD 多线程工作生活

深入浅出iOS多线程(三)——GCD多线程

2019-07-02  本文已影响0人  struggle3g

深入浅出iOS多线程(一)——线程的概念
深入浅出iOS多线程(二)——pthraed和NSThread的使用
深入浅出iOS多线程(三)——GCD多线程
深入浅出iOS多线程(四)——NSOperation多线程
深入浅出iOS多线程(五)——多线程锁

GCD简介

前面分析的pthread和NSThread多线程技术,直接操作线程,而下面分析的GCD以及下一篇NSOpration都是并发解决技术

什么是GCD

GCD的优势

GCD的两个核心概念

GCD的使用步骤

GCD简单使用

同步GCD

异步GCD

GCD的线程通信(GCD主线程)

dispatch_async(dispatch_get_global_queue(0, 0   ), ^{
    NSLog(@"%@",[NSThread currentThread]);
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
    
});

GCD子线程下载图片,主线程更新UI

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSURL * url = [NSURL URLWithString:@"https://images.unsplash.com/photo-1496840220025-4cbde0b9df65?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2734&q=80"];
    NSData * data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data];
    dispatch_async(dispatch_get_main_queue(), ^{
        self.imageView.image = image;
        [self.imageView sizeToFit];
        self.scrollView.contentSize = image.size;
    });
    
});

GCD队列

先把GCD异步、同步和队列、block的概念梳理一遍:

GCD串行队列

- (void)gcdQueueDemo1{
    
    //队列:串行
    /**
     1.队列名称
     2.队列的属性 :DISPATCH_QUEUE_SERIAL == NULL  标示串行
     **/
    
    dispatch_queue_t q = dispatch_queue_create("struggle3g", NULL);
    //2.同步执行任务
    for (int i = 0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@,%d",[NSThread currentThread],i);
        });
    }

}
- (void)gcdQueueDemo2{
    
    //队列:串行
    /**
     1.队列名称
     2.队列的属性 :DISPATCH_QUEUE_SERIAL == NULL  标示串行
     **/
    
    dispatch_queue_t q = dispatch_queue_create("struggle3g", NULL);
    //2.异步执行任务
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@,%d",[NSThread currentThread],i);
        });
    }
    
}

并行队列

全局队列

全剧队列创建API

dispatch_queue_global_t 
dispatch_get_global_queue(long identifier, unsigned long flags);

参数介绍:涉及到系统适配

提示:尽可能不要选择BACKGROUND 优先级,服务质量,线程执行会很慢

队列总结

GCD队列实际应用

例子:并发队列调度多个任务前,指定一个同步任务,让所有的异步任务,等待同步任务执行完成,这就是依赖关系

GCD延迟执行(GCD定时器)

代码如下:

/**
 
 参数:
 1.dispatch_time_t
 2.queue
 3.block
 */
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.00003 * NSEC_PER_SEC));
dispatch_after(when, dispatch_queue_create("struggle3g", NULL), ^{
    NSLog(@"%@",[NSThread currentThread]);
});

GCD一次执行

//苹果提供的 一次执行机制,不仅能够保证一次执行!而且是线程安全的!!
static dispatch_once_t onceToken;
NSLog(@"%ld",onceToken);
//苹果推荐使用 gcd 一次执行,效率高
//不要使用互斥锁,效率低!
dispatch_once(&onceToken, ^{
    //只会执行一次!!
    NSLog(@"执行了%@",[NSThread currentThread]);
});

GCD调度组(Group)

//1.队列
dispatch_queue_t  q = dispatch_get_global_queue(0, 0);
//2.调用组
dispatch_group_t  g = dispatch_group_create();
    
//3.添加任务,让队列调度,任务执行情况,最后通知群组
dispatch_group_async(g, q, ^{
    NSLog(@"downloadA %@",[NSThread currentThread]);
});
dispatch_group_async(g, q, ^{
    [NSThread sleepForTimeInterval:5];
    NSLog(@"downloadB %@",[NSThread currentThread]);
});
dispatch_group_async(g, q, ^{
    NSLog(@"downloadC %@",[NSThread currentThread]);
});
//4.所有任务执行完毕,通知
//dispatch_group_notify 本身也是异步的
dispatch_group_notify(g, q, ^{
    NSLog(@"OK %@",[NSThread currentThread]);
});
    
//如果想要dispatch_group_notify直接在主线程需要在其中传入主线程队列
dispatch_group_notify(g, dispatch_get_main_queue(), ^{
    NSLog(@"OK %@",[NSThread currentThread]);
});

GCD主队列

dispatch_queue_t mainqueue = dispatch_get_main_queue();

主队列同步任务

主线程异步任务

//1.队列 --> 已启动主线程,就可以获取主队列
dispatch_queue_t q = dispatch_get_main_queue();
    
//2.异步任务
dispatch_async(q, ^{
    NSLog(@"%@",[NSThread currentThread]);
});
NSLog(@"come here");

主线程同步任务(不死锁)

void (^task)(void) = ^{
    NSLog(@"这里!!%@",[NSThread currentThread]);
    //1.队列 --> 已启动主线程,就可以获取主队列
    dispatch_queue_t q = dispatch_get_main_queue();
    
    //2.同步任务
    dispatch_sync(q, ^{
        NSLog(@"主线程 %@",[NSThread currentThread]);
    });
    
    NSLog(@"come here");
};
    
//开启一个线程
dispatch_async(dispatch_get_global_queue(0, 0), task);

GCD(Apply)重复调用

全局并发队列中的Apply

/*
全局并发队列
结果验证:并发队列,开启子线程,乱序执行
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_apply(10, queue, ^(size_t count) {
    NSLog(@"count = %zu,%@",count, [NSThread currentThread]);
});

/*
 并发队列
 结果验证:并发队列,开启子线程,乱序执行
 */
dispatch_queue_t queue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, queue, ^(size_t count) {
    NSLog(@"count = %zu,%@",count, [NSThread currentThread]);
});

/*
 串行队列
 结果验证:串行队列,按顺序执行
 */
dispatch_queue_t queue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_SERIAL);
dispatch_apply(10, queue, ^(size_t count) {
    NSLog(@"count = %zu,%@",count, [NSThread currentThread]);
});

注意主线程队列中的Apply

GCD(dispatch_barrier_async())

dispatch_barrier_async顾名思义就是栅栏函数,就是等于在一个并发队列中,在多个任务之间增加一道栅栏,当栅栏以及栅栏之前的任务完成以后还能执行,栅栏以后的代码。

```
 dispatch_queue_t queue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    NSLog(@"++1++%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"++2++%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"++3++%@",[NSThread currentThread]);
});

dispatch_barrier_async(queue, ^{
    
    NSLog(@"栅栏函数");
});

dispatch_async(queue, ^{
    NSLog(@"++4++%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"++5++%@",[NSThread currentThread]);
});
```

输出结果一定是 1、2、3前面三个部分顺序执行,第四个一定是栅栏函数,后面2个顺序打乱。

dispatch_barrier_asyncdispatch_barrier_sync的区别就是阻不阻当前线程

GCD信号量操作

信号量的API

dispatch_semaphore_t dispatch_semaphore_create(long value);
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

并发队列,异步执行实现同步操作

首先建立一个并发队列,异步执行的操作

dispatch_queue_t queue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    NSLog(@"++1++%@",[NSThread currentThread]);
    
});
dispatch_async(queue, ^{
    NSLog(@"++2++%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"++3++%@",[NSThread currentThread]);
});

运行结果

 ++3++<NSThread: 0x600003b7c840>{number = 4, name = (null)}
 ++2++<NSThread: 0x600003b6d640>{number = 5, name = (null)}
 ++1++<NSThread: 0x600003b77a80>{number = 3, name = (null)}

从上述结果来看,队列中的任务并不是同步执行的,那么我们现在就可以使用信号量在进行同步操作,代码如下:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_queue_create("struggle3g", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    NSLog(@"++1++%@",[NSThread currentThread]);
    dispatch_semaphore_signal(semaphore);
    
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
dispatch_async(queue, ^{
    NSLog(@"++2++%@",[NSThread currentThread]);
    dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
    NSLog(@"++3++%@",[NSThread currentThread]);
});

打印结果如下:

++1++<NSThread: 0x6000029e73c0>{number = 3, name = (null)}
++2++<NSThread: 0x6000029ef280>{number = 4, name = (null)}
++3++<NSThread: 0x6000029ef280>{number = 4, name = (null)}

让多个异步任务按照同步顺序执行,直接在同步队列异步操作不是更好吗?为什么要使用复杂的信号量,这是因为,同步队列中当中只是在一个线程当中运行,而信号量可以充分利用多线程,它会开启子线程。所以同步队列异步操作丧失了并发执行的可能性。虽然可以完成任务,但是却没有充分发挥CPU多线程的优势

GCD结构图

GCD结构图.png
上一篇下一篇

猜你喜欢

热点阅读