`

GCD、Dispatch Queue系列(二)

    博客分类:
  • ios
 
阅读更多

为了在单一进程中充分发挥多核的优势,我们有必要使用多线程技术(我们没必要去提多进程,这玩意儿和GCD没关系)。在低层,GCD全局 dispatch queue仅仅是工作线程池的抽象。这些队列中的Block一旦可用,就会被dispatch到工作线程中。提交至用户队列的Block最终也会通过全局 队列进入相同的工作线程池(除非你的用户队列的目标是主线程,但是为了提高运行速度,我们绝不会这么干)。

有两种途径来通过GCD“榨取”多核心系统的性能:将单一任务或者一组相关任务并发至全局队列中运算;将多个不相关的任务或者关联不紧密的任务并发至用户队列中运算;

全局队列

设想下面的循环:

1
2
for(id obj in array)
    [self doSomethingIntensiveWith:obj];

假定 -doSomethingIntensiveWith: 是线程安全的且可以同时执行多个.一个array通常包含多个元素,这样的话,我们可以很简单地使用GCD来平行运算:

1
2
3
4
5
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for(id obj in array)
    dispatch_async(queue, ^{
        [self doSomethingIntensiveWith:obj];
    });

如此简单,我们已经在多核心上运行这段代码了。 

当然这段代码并不完美。有时候我们有一段代码要像这样操作一个数组,但是在操作完成后,我们还需要对操作结果进行其他操作:

1
2
3
for(id obj in array)
    [self doSomethingIntensiveWith:obj];
[self doSomethingWith:array];

这时候使用GCD的 dispatch_async 就悲剧了.我们还不能简单地使用dispatch_sync来解决这个问题, 因为这将导致每个迭代器阻塞,就完全破坏了平行计算。

解决这个问题的一种方法是使用dispatch group。一个dispatch group可以用来将多个block组成一组以监测这些Block全部完成或者等待全部完成时发出的消息。使用函数 dispatch_group_create来创建,然后使用函数dispatch_group_async来将block提交至一个dispatch queue,同时将它们添加至一个组。所以我们现在可以重新编码:

1
2
3
4
5
6
7
8
9
10
dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array)
    dispatch_group_async(group, queue, ^{
        [self doSomethingIntensiveWith:obj];
    });
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
 
[self doSomethingWith:array];

如果这些工作可以异步执行,那么我们可以更风骚一点,将函数-doSomethingWith:放在后台执行。我们使用dispatch_group_async函数建立一个block在组完成后执行:

1
2
3
4
5
6
7
8
9
10
dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for(id obj in array)
    dispatch_group_async(group, queue, ^{
        [self doSomethingIntensiveWith:obj];
    });
dispatch_group_notify(group, queue, ^{
    [self doSomethingWith:array];
});
dispatch_release(group);

不仅所有数组元素都会被平行操作,后续的操作也会异步执行,并且这些异步运算都会将程序的其他部分的负载考虑在内。注意如果-doSomethingWith:需要在主线程中执行,比如操作GUI,那么我们只要将main queue而非全局队列传给dispatch_group_notify函数就行了。

 

对于同步执行,GCD提供了一个简化方法叫做dispatch_apply。这个函数调用单一block多次,并平行运算,然后等待所有运算结束,就像我们想要的那样:

1
2
3
4
5
dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply([array count], queue, ^(size_t index){
        [self doSomethingIntensiveWith:[array objectAtIndex:index]];
    });
    [self doSomethingWith:array];

这很棒,但是异步咋办?dispatch_apply函数可是没有异步版本的。但是我们使用的可是一个为异步而生的API啊!所以我们只要用dispatch_async函数将所有代码推到后台就行了:

1
2
3
4
5
6
7
dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
    dispatch_apply([array count], queue, ^(size_t index){
        [self doSomethingIntensiveWith:[array objectAtIndex:index]];
    });
    [self doSomethingWith:array];
});

简单的要死!

 

这种方法的关键在于确定我们的代码是在一次对不同的数据片段进行相似的操作。如果你确定你的任务是线程安全的(不在本篇讨论范围内)那么你可以使用GCD来重写你的循环了,更平行更风骚。

要看到性能提升,你还得进行一大堆工作。比之线程,GCD是轻量和低负载的,但是将block提交至queue还是很消耗资源的——block需要 被拷贝和入队,同时适当的工作线程需要被通知。不要将一张图片的每个像素作为一个block提交至队列,GCD的优点就半途夭折了。如果你不确定,那么请 进行试验。将程序平行计算化是一种优化措施,在修改代码之前你必须再三思索,确定修改是有益的(还有确保你修改了正确的地方)。

Subsystem并发运算

前面的章节我们讨论了在程序的单个subsystem中发挥多核心的优势。下来我们要跨越多个子系统。

例如,设想一个程序要打开一个包含meta信息的文档。文档数据本身需要解析并转换至模型对象来显示,meta信息也需要解析和转换。但是,文档数 据和meta信息不需要交互。我们可以为文档和meta各创建一个dispatch queue,然后并发执行。文档和meta的解析代码都会各自串行执行,从而不用考虑线程安全(只要没有文档和meta之间共享的数据),但是它们还是并 发执行的。

一旦文档打开了,程序需要响应用户操作。例如,可能需要进行拼写检查、代码高亮、字数统计、自动保存或者其他什么。如果每个任务都被实现为在不同的 dispatch queue中执行,那么这些任务会并发执行,并各自将其他任务的运算考虑在内(respect to each other),从而省去了多线程编程的麻烦。

使用dispatch source(下次我会讲到),我们可以让GCD将事件直接传递给用户队列。例如,程序中监视socket连接的代码可以被置于它自己的dispatch queue中,这样它会异步执行,并且执行时会将程序其他部分的运算考虑在内。另外,如果使用用户队列的话,这个模块会串行执行,简化程序。

结论

我们讨论了如何使用GCD来提升程序性能以及发挥多核系统的优势。尽管我们需要比较谨慎地编写并发程序,GCD还是使得我们能更简单地发挥系统的可用计算资源。

下一篇中,我们将讨论dispatch source,也就是GCD的监视内部、外部事件的机制。

分享到:
评论

相关推荐

    并发编程之Operation Queue和GCD

    GCD支持同步或异步任务处理,串行或并行的处理队列(Dispatch Queue),非系统调用的信号量机制,定时任务处理,进程、文件或网络的监听任务等。 Dispatch Queue是一个任务执行队列,可以让你异步或同步地执行多个...

    ios demo,dispatch_async,DISPATCH_QUEUE_CONCURRENT,多任务并发执行,自动创建多线程

    压缩包中的`test_gcd_thread_asynchronous_concurrent`可能是一个测试项目,包含了一些用于演示`dispatch_async`和`DISPATCH_QUEUE_CONCURRENT`使用的代码文件。在这个项目中,开发者可能可以看到如何创建并行队列、...

    gcdTest下载图片 dispatch_async

    dispatch_queue_t是GCD的核心,分为串行队列(serial queue)和并行队列(concurrent queue)。串行队列一次只执行一个任务,而并行队列则可以同时处理多个任务。在iOS中,有全局并行队列和主队列两种。全局并行队列...

    iOS开发:深入理解GCD 第二篇(dispatch_group、dispatch_barrier、基于线程安全的多读单写)1

    在iOS开发中,Grand Central Dispatch (GCD) 是一种强大的多线程管理工具,它能够帮助开发者高效地处理并发任务。本篇文章主要讨论了如何深入理解GCD中的dispatch_group、dispatch_barrier以及基于线程安全的多读单...

    iOS GCD多核编程

    let queue = DispatchQueue.global(qos: .background) queue.async { // 在这里执行耗时操作,如下载数据 // ... // 完成后更新UI,但必须回到主线程 DispatchQueue.main.async { // 更新UI } } ``` 四、...

    Cocoa多线程编程之 block 与 dispatch quene

    苹果的Grand Central Dispatch (GCD) 提供了dispatch queue,用于调度block在不同的线程上执行。主要有两种类型的dispatch queue:串行队列(serial queue)和并行队列(concurrent queue)。 1. 串行队列:每个...

    iOS-常驻线程之线程循环调用(两个线程循环交替调用)

    let queue1 = DispatchQueue(label: "thread1", qos: .background, attributes: .concurrent) let queue2 = DispatchQueue(label: "thread2", qos: .background, attributes: .concurrent) let timerSource = ...

    iOS GCD详解

    Dispatch Queues是GCD的基本概念,dispatch queue是一个对象,它可以接受任务,并将任务以先到先执行的顺序来执行。dispatch queue可以是并发的或串行的。GCD中有三种队列类型: 1. 主队列(main queue):与主线程...

    iOS GCD 多线程

    全局队列具有四种优先级:`DISPATCH_QUEUE_PRIORITY_HIGH`、`DISPATCH_QUEUE_PRIORITY_DEFAULT`、`DISPATCH_QUEUE_PRIORITY_LOW` 和 `DISPATCH_QUEUE_PRIORITY_BACKGROUND`。虽然自定义队列本身不支持直接设置优先级...

    Constructing your own 地上爬梯好次 queues with GCD

    DispatchQueue.global(qos: .userInteractive) // 最高优先级 DispatchQueue.global(qos: .userInitiated) // 默认优先级 DispatchQueue.global(qos: .utility) // 较低优先级 DispatchQueue.global(qos: ....

    ios gcd

    在iOS开发中,Grand Central Dispatch(GCD)是苹果公司提供的多线程解决方案,它极大地简化了并发编程。GCD是基于C语言的API,但同时也很好地与Objective-C和Swift兼容。这篇博客文章《ios gcd》可能深入探讨了如何...

    多线程GCD,NSThread,NSOperationQueue,详细解释Demo

    例如,可以使用`DispatchQueue.main.async`、`-[NSThread performSelectorOnMainThread:]`或`-[NSOperationQueue addOperationWithBlock:]`。 综上所述,GCD、NSThread和NSOperationQueue各有优缺点,选择哪种取决...

    iOS GCD 开发教程 完整版

    在Swift中,GCD的API被封装在`DispatchQueue`类中,使用更加简洁。例如,创建并行队列并提交异步任务: ```swift let globalQueue = DispatchQueue.global(qos: .background) globalQueue.async { // 异步执行的...

    GCD基本概念

    GCD的核心概念和特性包括Dispatch Queue、Dispatch Source、以及高效的内存管理机制。 ### GCD基本概念与特性 1. **GCD与NSOperationQueue的对比:** GCD和NSOperationQueue都可以将任务分解并排队,从而并发或...

    GCD封装成面向对象

    private let queue: DispatchQueue init(queueType: QueueType) { if queueType == .serial { queue = DispatchQueue(label: "com.example.serialQueue", attributes: .serial) } else { queue = Dispatch...

    iOS应用程序中通过dispatch队列控制线程执行的方法

    GCD编程的核心就是dispatch队列,dispatch block的执行最终都会放进某个队列中去进行,它类似NSOperationQueue但更复杂也更强大,并且可以嵌套使用。所以说,结合block实现的GCD,把函数闭包(Closure)的特性发挥得...

    GCD使用的相关方法

    - **Dispatch Queue**:调度队列是GCD的核心,用于管理任务的执行。主要有两种类型:串行队列(Serial Queue)和并行队列(Concurrent Queue)。串行队列一次仅执行一个任务,而并行队列则可以同时处理多个任务。 ...

    IOS 多线程GCD详解

    Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。 dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispatch_get_main_queue获取。 #definedispatch_get_main_queue() \DISPATCH...

    gcd简介及应用实例及实例分析.txt

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ // 执行任务 }); ``` 2. **创建本地串行队列**: ```swift dispatch_queue_t queue ...

Global site tag (gtag.js) - Google Analytics