乐趣区

关于ios:iOS多线程之GCDOperationQueue-对比和实践记录

简介

     在计算的晚期,计算机能够执行的最大工作量是由 CPU 的时钟速度决定的。然而随着技术的提高和处理器设计的紧凑化,热量和其余物理束缚开始限度处理器的最大时钟速度。因而,芯片制造商寻找其余办法来进步芯片的总体性能。他们决定的解决方案是减少每个芯片上的处理器外围数量。通过减少内核的数量,一个独自的芯片能够每秒执行更多的指令,而不必减少 CPU 的速度或扭转芯片的大小或热个性。惟一的问题是如何利用额定的内核。

iOS 开发交换技术群:563513413,不论你是大牛还是小白都欢送入驻,分享 BAT, 阿里面试题、面试教训,探讨技术,大家一起交流学习成长!

     应用程序应用多核的传统办法是创立多个线程。与依赖线程不同,iOS 采纳异步设计办法来解决并发问题。通常,这项工作波及获取一个后盾线程,在该线程上启动所需的工作,而后在工作实现时向调用方发送告诉(通常通过一个回调函数)。

     iOS 提供了一些技术,容许您异步执行任何工作,而无需本人治理线程。异步启动工作的技术之一是 Grand Central Dispatch (GCD)。这种技术采纳线程治理代码,并将该代码移动到零碎级别。您所要做的就是定义要执行的工作,并将它们增加到适当的分派队列中。GCD 负责创立所需的线程,并安顿工作在这些线程上运行。因为线程治理当初是零碎的一部分,GCD 提供了工作治理和执行的整体办法,比传统线程提供了更高的效率。

     OperationQueue(操作队列,api 类名为 NSOperationQueue)是 Objective-C 对象,是对 GCD 的封装。其作用十分相似于分派队列。您定义要执行的工作,而后将它们增加到 OperationQueue 中,OperationQueue 解决这些工作的调度和执行。与 GCD 一样,OperationQueue 为您解决所有线程治理,确保在零碎上尽可能疾速无效地执行工作。

GCD、OperationQueue 比照

核心理念

  • GCD 的外围概念:将 工作(block) 增加到队列,并且指定执行工作的函数。
  • NSOperation 的外围概念:把 操作(异步) 增加到 队列。

区别

  • GCD:

    • 将工作(block)增加到队列(串行 / 并发 / 主队列),并且指定工作执行的函数(同步 / 异步)
    • GCD 是底层的 C 语言形成的 API
    • iOS 4.0 推出的,针对多核处理器的并发技术
    • 在队列中执行的是由 block 形成的工作,这是一个轻量级的数据结构
    • 要进行曾经退出 queue 的 block 须要写简单的代码
    • 须要通过 Barrier(dispatch_barrier_async)或者同步工作设置工作之间的依赖关系
    • 只能设置队列的优先级
    • 高级性能:dispatch_once_t(一次性执行, 多线程平安);dispatch_after(提早);dispatch_group(调度组);dispatch_semaphore(信号量);dispatch_apply(优化程序不敏感大体量 for 循环);
  • OperationQueue:

    • OC 框架,更加面向对象,是对 GCD 的封装。
    • iOS 2.0 推出的,苹果推出 GCD 之后,对 NSOperation 的底层进行了全副重写。
    • 能够设置队列中每一个操作的 QOS()队列的整体 QOS
    • 操作相干 Operation 作为一个对象,为咱们提供了更多的抉择:工作依赖(addDependency),能够跨队列设置操作的依赖关系;在队列中的优先级(queuePriority)服务质量(qualityOfService, iOS8+); 实现回调(void (^completionBlock)(void)
    • 队列相干 服务质量(qualityOfService, iOS8+); 最大并发操作数(maxConcurrentOperationCount),GCD 不易实现; 暂停 / 持续(suspended);勾销所有操作(cancelAllOperations);KVO 监听队列工作执行进度(progress, iOS13+);

     接下来通过文字,联合实际代码(工程链接在文末)和运行成果 gif 图对局部性能进行剖析。

GCD

队列

串行队列(Serial Queues)

     串行队列中的工作按程序执行;然而不同串行队列间没有任何束缚;多个串行队列同时执行时,不同队列中工作执行是并发的成果。比方:火车站买票能够有多个卖票口,然而每个排的队都是串行队列,整体并发,复线串行。

     留神防坑:串行队列创立的地位。比方上面代码示例中:在 for 循环外部创立时,每个循环都是创立一个新的串行队列,外面只装一个工作,多个串行队列,后果整体上是并发的成果。想要串行成果,必须在 for 循环内部创立串行队列。

     串行队列适宜治理共享资源。保障了程序拜访,杜绝了资源竞争。

      代码示例:

 `private func serialExcuteByGCD(){let lArr : [UIImageView] = [imageView1, imageView2, imageView3, imageView4]

        // 串行队列,异步执行时,只开一个子线程
        let serialQ = DispatchQueue.init(label: "com.companyName.serial.downImage")

        for i in 0..<lArr.count{let lImgV = lArr[i]

            // 清空旧图片
            lImgV.image = nil

         // 留神,防坑:串行队列创立的地位, 在这创立时,每个循环都是一个新的串行队列,外面只装一个工作,多个串行队列,整体上是并行的成果。//            let serialQ = DispatchQueue.init(label: "com.companyName.serial.downImage")

            serialQ.async {print("第 (i) 个 开始,%@",Thread.current)
                Downloader.downloadImageWithURLStr(urlStr: imageURLs[i]) {(img) in
                    let lImgV = lArr[i]

                    print("第 (i) 个 完结")
                    DispatchQueue.main.async {print("第 (i) 个 切到主线程更新图片")
                        lImgV.image = img
                    }
                    if nil == img{print("第 (i+1) 个 img is nil")
                    }
                }
            }
        }
    }` 复制代码

gif 效果图:

图中下载时可顺利拖动滚动条,是为了阐明下载在子线程,不影响 UI 交互

log:

` 第 0 个 开始
第 0 个 完结
第 1 个 开始
第 0 个 更新图片
第 1 个 完结
第 2 个 开始
第 1 个 更新图片
第 2 个 完结
第 3 个 开始
第 2 个 更新图片
第 3 个 完结
第 3 个 更新图片 ` 复制代码

      由 log 可知: GCD 切到主线程也须要工夫,切换实现之前,指令可能曾经执行到下个循环了。然而看起来图片还是顺次下载实现和显示的,因为每一张图切到主线程显示都须要工夫。

并发队列(Concurrent Queues)

     并发队列仍旧保障中工作按退出的先后顺序开始(FIFO),然而无奈晓得执行程序,执行时长和某一时刻的工作数。按 FIFO 开始后,他们之间不会互相期待。

     比方:提交了 #1,#2,#3 工作到并发队列,开始的程序是 #1,#2,#3。#2 和 #3 尽管开始的比 #1 晚,然而可能比 #1 执行完结的还要早。工作的执行是由零碎决定的,所以执行时长和完结工夫都无奈确定。

     须要用到并发队列时,强烈建议 应用零碎自带的四种全局队列之一。然而,当你须要应用 barrier 对队列中工作进行栅栏时,只能应用自定义并发队列。

Use a barrier to synchronize the execution of one or more tasks in your dispatch queue. When you add a barrier to a concurrent dispatch queue, the queue delays the execution of the barrier block (and any tasks submitted after the barrier) until all previously submitted tasks finish executing. After the previous tasks finish executing, the queue executes the barrier block by itself. Once the barrier block finishes, the queue resumes its normal execution behavior.

     比照:barrier 和锁的区别

  • 依赖对象不同,barrier 依赖的对象是自定义并发队列,锁操作依赖的对象是线程。
  • 作用不同,barrier 起到自定义并发队列中栅栏的作用;锁起到多线程操作时避免资源竞争的作用。

      代码示例:

`private func concurrentExcuteByGCD(){let lArr : [UIImageView] = [imageView1, imageView2, imageView3, imageView4]

        for i in 0..<lArr.count{let lImgV = lArr[i]

            // 清空旧图片
            lImgV.image = nil

            // 并行队列: 图片下载工作按程序开始,然而是并行执行,不会互相期待,工作完结和图片显示程序是无序的,多个子线程同时执行,性能更佳。let lConQ = DispatchQueue.init(label: "cusQueue", qos: .background, attributes: .concurrent)
            lConQ.async {print("第 (i) 个开始,%@", Thread.current)
                Downloader.downloadImageWithURLStr(urlStr: imageURLs[i]) {(img) in
                    let lImgV = lArr[i]
                      print("第 (i) 个完结")
                    DispatchQueue.main.async {lImgV.image = img}
                    if nil == img{print("第 (i+1) 个 img is nil")
                    }
                }
            }
        }
    }` 复制代码

gif 效果图:

log:

` 第 0 个开始,%@ <NSThread: 0x600002de2e00>{number = 4, name = (null)}
第 1 个开始,%@ <NSThread: 0x600002dc65c0>{number = 6, name = (null)}
第 2 个开始,%@ <NSThread: 0x600002ddc8c0>{number = 8, name = (null)}
第 3 个开始,%@ <NSThread: 0x600002d0c8c0>{number = 7, name = (null)}
第 0 个完结
第 3 个完结
第 1 个完结
第 2 个完结 ` 复制代码

串行、并发队列比照图

注意事项

  • 无论串行还是并发队列,都是 FIFO;个别创立 工作(blocks)和加工作到队列是在主线程,然而工作执行个别是在其余线程(asyc)。须要刷新 UI 时,如果以后不再主线程,须要切回主线程执行。当不确定以后线程是否在主线程时,能够应用上面代码:

    `/**
    Submits a block for asynchronous execution on a main queue and returns immediately.
    */
    static inline void dispatch_async_on_main_queue(void (^block)()) {if (NSThread.isMainThread) {block();
    } else {dispatch_async(dispatch_get_main_queue(), block);
    }
    }` 复制代码
  • 主队列是串行队列,每个工夫点只能有一个工作执行,因而如果耗时操作放到主队列,会导致界面卡顿。
  • 零碎提供一个串行主队列,4 个 不同优先级的全局队列。用 dispatch_get_global_queue 办法获取全局队列时,第一个参数有 4 种类型可选:

    • DISPATCH_QUEUE_PRIORITY_HIGH
    • DISPATCH_QUEUE_PRIORITY_DEFAULT
    • DISPATCH_QUEUE_PRIORITY_LOW
    • DISPATCH_QUEUE_PRIORITY_BACKGROUND
  • 串行队列异步执行时,切到主线程刷 UI 也须要工夫,切换实现之前,指令可能曾经执行到下个循环了。然而看起来图片还是顺次下载实现和显示的,因为每一张图切到主线程显示都须要工夫。详见 demo 示例。
  • iOS8 之后,如果须要增加可被勾销的工作,能够应用 DispatchWorkItem 类,此类有 cancel 办法。
  • 应该防止创立大量的串行队列, 如果心愿并发执行大量工作,请将它们提交给全局并发队列之一。创立串行队列时,请尝试 为每个队列确定一个用处,例如爱护资源或同步应用程序的某些要害行为(如蓝牙检测后果须要有序解决的逻辑)。

block(块)相干

     调度队列复制增加到它们中的块,并在执行实现时开释块。尽管队列在执行小工作时比原始线程更无效,然而创立块并在队列上执行它们依然存在开销。如果一个块执行的工作量太少,那么内联执行它可能比将它分派到队列中要便宜得多。判断一个块是否工作量太少的办法是使用性能工具为每个门路收集度量数据并进行比拟。您可能心愿将 block 的局部代码蕴含在 @autoreleasepool 中,以解决这些对象的内存治理。只管 GCD 调度队列领有本人的主动开释池,但它们不能保障这些池何时耗尽。如果您的应用程序是内存受限的,那么创立您本人的主动开释池能够让您以更有法则的距离开释主动开释对象的内存。

dispatch_after

     dispatch_after 函数并不是在指定工夫之后才开始执行解决,而是在指定工夫之后将工作追加到队列中。这个工夫并不是相对精确的。代码示例:

`dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@"2s 后执行");
    });` 复制代码

dispatch_semaphore

      在多线程拜访可变变量时,是非线程平安的。可能导致程序解体。此时,能够通过应用信号量(semaphore)技术,保障多线程解决某段代码时,前面线程期待后面线程执行,保障了多线程的安全性。应用办法记两个就行了,一个是 wait(dispatch_semaphore_wait),一个是 signal(dispatch_semaphore_signal)。

具体请参考文章 Semaphore 回顾

dispatch_apply

     当每次迭代中执行工作与其余所有迭代中执行的工作不同,且每个循环实现的程序不重要时,能够用 dispatch_apply 函数替换循环。留神:替换后,dispatch_apply 函数整体上是同步执行,外部 block 的执行类型(串行 / 并发)由队列类型决定,然而串行队列易死锁,倡议用并发队列。

原循环:

`for (i = 0; i < count; i++) {printf("%un",i);
}
printf("done");` 复制代码

优化后:

`dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

 //count 是迭代的总次数。dispatch_apply(count, queue, ^(size_t i) {printf("%un",i);
});

// 同样在下面循环完结后才调用。printf("done");` 复制代码

     您应该确保您的工作代码在每次迭代中实现正当数量的工作。与您分派到队列的任何块或函数一样,调度该代码以便执行会带来开销。如果循环的每次迭代只执行大量的工作,那么调度代码的开销可能会超过将代码分派到队列可能带来的性能劣势。如果您在测试期间发现这一点是正确的,那么您能够应用步进来减少每个循环迭代期间执行的工作量。通过大步后退,您能够将原始循环的多个迭代集中到一个块中,并按比例缩小迭代次数。例如,如果您最后执行了 100 次 迭代,但决定应用步长为 4 的迭代,那么您当初从每个块执行 4 次循环迭代,迭代次数为 25 次。

自问自答

  • 一个队列的不同工作能够在多个线程执行吗?答:串行队列,异步执行时,只开一个子线程;无所谓多个线程执行;并发队列,异步执行时,会主动开多个线程,能够在多个线程并发执行不同的工作。
  • 一个线程能够同时执行多个队列的工作吗?答:一个线程某个工夫点只能执行一个工作,执行结束后,可能执行到来自其余队列的工作(如果有的话)。比方:主线程除了执行主队列中工作外,也可能会执行非主队列中的工作。

    队列与线程关系示例图:

  • qualityOfService 和 queuePriority 的区别是什么?答:qualityOfService:      用于示意 operation 在获取系统资源时的优先级,默认值:NSQualityOfServiceBackground,咱们能够依据须要给 operation 赋不同的优化级,如最高优化级:NSQualityOfServiceUserInteractive。queuePriority:      用于设置 operation 在 operationQueue 中的绝对优化级,同一 queue 中优化级高的 operation(isReady 为 YES) 会被优先执行。须要留神辨别 qualityOfService (在零碎层面,operation 与其余线程获取资源的优先级) 与 queuePriority (同一 queue 中 operation 间执行的优化级)的区别。同时,须要留神 dependencies (严格控制执行程序)与 queuePriority (queue 外部绝对优先级)的区别。
  • 增加依赖后,队列中网络申请工作有依赖关系时,工作完结断定以数据返回为准还是以发动申请为准?答:以发动申请为准。剖析过程详见 NSOperationQueue 队列中操作依赖相干思考

OperationQueue

  • NSOperation      NSOperation 是一个 ” 抽象类 ”,不能间接应用。抽象类的用途是定义子类共有的属性和办法。NSOperation 是基于 GCD 做的面向对象的封装。相比拟 GCD 应用更加简略,并且提供了一些用 GCD 不是很好实现的性能。是苹果公司举荐应用的并发技术。它有两个子类:

    • NSInvocationOperation (调用操作)
    • NSBlockOperation (块操作)      个别罕用 NSBlockOperation,代码简略,同时因为闭包性使它没有传参问题。工作被封装在 NSOperation 的子类实例类对象里,一个 NSOperation 子类对象能够增加多个工作 block 和 一个执行实现 block,当其关联的所有 block 执行完时,就认为操作完结了。
  • NSOperationQueue       OperationQueue 也是对 GCD 的高级封装,更加面向对象,能够实现 GCD 不不便实现的一些成果。被增加到队列的操作默认是异步执行的。

PS:常见的抽象类有:

  • UIGestureRecognizer
  • CAAnimation
  • CAPropertyAnimation

    能够实现 非 FIFO 成果

    通过对不同操作设置依赖, 或优先级,可实现 非 FIFO 成果。代码示例:

`func testDepedence(){
        let op0 = BlockOperation.init {print("op0")
        }

        let op1 = BlockOperation.init {print("op1")
        }

        let op2 = BlockOperation.init {print("op2")
        }

        let op3 = BlockOperation.init {print("op3")
        }

        let op4 = BlockOperation.init {print("op4")
        }

        op0.addDependency(op1)
        op1.addDependency(op2)

        op0.queuePriority = .veryHigh
        op1.queuePriority = .normal
        op2.queuePriority = .veryLow

        op3.queuePriority = .low
        op4.queuePriority = .veryHigh

        gOpeQueue.addOperations([op0, op1, op2, op3, op4], waitUntilFinished: false)
    }` 复制代码

log:

 `op4
 op2
 op3
 op1
 op0` 复制代码

 `op4
 op3
 op2
 op1
 op0` 复制代码

阐明:操作间不存在依赖时,按优先级执行;存在依赖时,按依赖关系先后执行(与无依赖关系的其余工作相比,依赖汇合的执行程序不确定)

队列暂停 / 持续

通过对队列的 isSuspended 属性赋值,可实现队列中未执行工作的暂停和持续成果。正在执行的工作不受影响。

`/// 暂停队列,只对未执行中的工作无效。本例中对串行队列的成果显著。并发队列因 4 个工作一开始就很容易一起开始执行,即便挂起也无奈影响已处于执行状态的工作。@IBAction func pauseQueueItemDC(_ sender: Any) {gOpeQueue.isSuspended = true}

    /// 复原队列,之前未开始执行的工作会开始执行
    @IBAction func resumeQueueItemDC(_ sender: Any) {gOpeQueue.isSuspended = false}` 复制代码

gif 效果图:

勾销操作

  • 一旦增加到操作队列中,操作对象实际上归队列所有,不能删除。勾销操作的惟一办法是勾销它。能够通过调用单个操作对象的 cancel 办法来勾销单个操作对象,也能够通过调用队列对象的 cancelAllOperations 办法来勾销队列中的所有操作对象。
  • 更常见的做法是勾销所有队列操作,以响应某些重要事件,如应用程序退出或用户专门申请勾销,而不是有选择地勾销操作。

勾销单个操作对象

勾销(cancel)时,有 3 种状况:1. 操作在队列中期待执行,这种状况下,操作将不会被执行。2. 操作曾经在执行中,此时,零碎不会强制进行这个操作,然而,其 cancelled属性会被置为 true。3. 操作已实现,此时,cancel 无任何影响。

勾销队列中的所有操作对象

办法:cancelAllOperations。同样只会对未执行的工作无效。demo 中代码:

 `deinit {gOpeQueue.cancelAllOperations()
        print("die:%@",self)
    }` 复制代码

自问自答

  • 通过设置操作间依赖,能够实现 非 FIFO 的指定程序成果。那么,通过设置最大并发数为 1,能够实现指定程序成果吗?A: 不能够!设置最大并发数为 1 后,尽管每个工夫点只执行一个操作,然而操作的执行程序依然基于其余因素,如操作的依赖关系,操作的优先级(依赖关系比优先级级别更高,即先依据依赖关系排序; 不存在依赖关系时,才依据优先级排序)。因而,序列化 操作队列 不会提供与 GCD 中的序列 分派队列 完全相同的行为。如果操作对象的执行程序对您很重要,那么您应该在将操作增加到队列之前应用 依赖关系 建设该程序,或改用 GCD 的 串行队列 实现序列化成果。
  • Operation Queue 的 block 中为何无需应用 [weak self] 或 [unowned self]?A: 即便队列对象是为全局的,self -> queue -> operation block -> self,确实会造成循环援用。然而在队列里的操作执行结束时,队列会主动开释操作,主动解除循环援用。所以不用应用 [weak self] 或 [unowned self]。此外,这种循环援用在某些状况下十分有用,你无需额定持有任何对象就能够让操作主动实现它的工作。比方下载页面下载过程中,退出有循环援用的界面时,如果不执行 cancelAllOperation 办法,能够实现继续执行残余队列中下载工作的成果。

func addOperation(_ op: Operation) Discussion: Once added, the specified operation remains in the queue until it finishes executing. Declaration

func addOperation(_ block: @escaping () -> Void) Parameters block The block to execute from the operation. The block takes no parameters and has no return value. Discussion This method adds a single block to the receiver by first wrapping it in an operation object. You should not attempt to get a reference to the newly created operation object or determine its type information.

  • 操作的 QOS 和队列的 QOS 有何关系?A: 队列的 QOS 设置,会主动把较低优先级的操作晋升到与队列雷同优先级。(原更高优先级操作的优先级放弃不变)。后续增加进队列的操作,优先级低于队列优先级时,也会被主动晋升到与队列雷同的优先级。留神,苹果文档如下的解释是谬误的 This property specifies the service level applied to operation objects added to the queue. If the operation object has an explicit service level set, that value is used instead. 起因详见:Can NSOperation have a lower qualityOfService than NSOperationQueue?

常见问题

如何解决资源竞争问题

资源竞争可能导致数据异样,死锁,甚至因拜访野指针而解体。

  • 对于有显著先后依赖关系的工作,最佳计划是 GCD 串行队列, 能够在不应用线程锁时保障资源互斥。
  • 其余状况,对存在资源竞争的代码加锁或应用信号量(初始参数填 1,示意只容许一条线程拜访资源)。
  • 串行队列同步执行时,如果有 工作互相期待,会死锁。比方:在主线程上同步执行工作时,因工作和之前已退出主队列但未执行的工作会互相期待,导致死锁。

    `func testDeadLock(){
        // 主队列同步执行,会导致死锁。block 须要期待 testDeadLock 执行,而主队列同步调用,又使其余工作必须期待此 block 执行。于是造成了互相期待,就死锁了。DispatchQueue.main.sync {print("main block")
        }
        print("2")
    }` 复制代码

然而上面代码不会死锁,故 串行队列同步执行工作不肯定死锁

`- (void)testSynSerialQueue{
    dispatch_queue_t myCustomQueue;
    myCustomQueue = dispatch_queue_create("com.example.MyCustomQueue", NULL);

    dispatch_async(myCustomQueue, ^{printf("Do some work here.n");
    });

    printf("The first block may or may not have run.n");

    dispatch_sync(myCustomQueue, ^{printf("Do some more work here.n");
    });
    printf("Both blocks have completed.n");
}` 复制代码

如何进步代码效率

“西饼传说”

代码设计优先级:零碎办法 > 并行 > 串行 > 锁,简记为:_西饼传说_

  • 尽可能依赖 零碎 框架。实现并发性的最佳办法是利用零碎框架提供的内置并发性。
  • 尽早辨认系列工作,并尽可能使它们更加 并行。如果因为某个工作依赖于某个共享资源而必须间断执行该工作,请思考更改体系结构以删除该共享资源。您能够思考为每个须要资源的客户机制作资源的正本,或者齐全打消该资源。
  • 不应用锁来爱护某些共享资源,而是指定一个 串行 队列 (或应用操作对象依赖项)以正确的程序执行工作。
  • 防止应用 GCD 调度队列 操作队列 提供的反对使得在大多数状况下不须要锁定。

确定操作对象的适当范畴

  • 只管能够向操作队列中增加任意大量的操作,但这样做通常是不切实际的。与任何对象一样,NSOperation 类的实例耗费内存,并且具备与其执行相干的实际成本。如果您的每个操作对象只执行大量的工作,并且您创立了数以万计的操作对象,那么您可能会发现,您花在调度操作上的工夫比花在理论工作上的工夫更多。如果您的应用程序曾经受到内存限度,那么您可能会发现,仅仅在内存中领有数万个操作对象就可能进一步升高性能。
  • 无效应用操作的要害是 在你须要做的工作量和放弃计算机繁忙之间找到一个适当的均衡。尽量确保你的业务做了正当的工作量。例如,如果您的应用程序创立了 100 个操作对象来对 100 个不同的值执行雷同的工作,那么能够思考创立 10 个操作对象来解决每个值。
  • 您还应该防止将大量操作一次性增加到队列中,或者防止间断地将操作对象增加到队列中的速度快于解决它们的速度。与其用操作对象吞没队列,不如批量创立这些对象。当一个批处理实现执行时,应用实现块通知应用程序创立一个新的批处理。当您有很多工作要做时,您心愿放弃队列中充斥足够的操作,以便计算机放弃繁忙,然而您不心愿一次创立太多操作,以至于应用程序耗尽内存。
  • 当然,您创立的操作对象的数量以及在每个操作对象中执行的工作量是可变的,并且齐全取决于您的应用程序。你应该常常应用像 Instruments 这样的工具来帮忙你在效率和速度之间找到一个适当的均衡。无关 Instruments 和其余可用于为代码收集度量规范的性能工具的概述,请参阅 性能概述。

术语解释摘录

  • 异步工作(asynchronous tasks):由一个线程启动,但实际上在另一个线程上运行,利用额定的处理器资源更快地实现工作。
  • 互斥(mutex):提供对共享资源的互斥拜访的锁。互斥锁一次只能由一个线程持有。试图获取由不同线程持有的互斥对象会使以后线程处于休眠状态,直到最终取得锁为止。
  • 过程(process):应用软件或程序的运行时实例。过程有本人的虚拟内存空间和系统资源(包含端口权限),这些资源独立于调配给其余程序的资源。一个过程总是蕴含至多一个线程(主线程),并且可能蕴含任意数量的其余线程。
  • 信号量(semaphore):限度对共享资源拜访的受爱护变量。互斥(Mutexes)和条件(conditions)都是不同类型的信号量。
  • 工作(task),示意须要执行的工作量。
  • 线程(thread):过程中的执行流程。每个线程都有本人的堆栈空间,但在其余方面与同一过程中的其余线程共享内存。
  • 运行循环(run loop): 一个事件处理循环,接管事件并派发到适当的处理程序。
退出移动版