文章出处,原创于 https://HawkingOuYang.github.io/
NSOperationQueue, GCD, NSOperation, NSThread, pthread
多线程 并发
并发与非并发: NSOperationQueue and concurrent vs non-concurrent
我们建议采纳的安全模式是这样的:从主线程中提取出要使用到的数据,并利用一个操作队列在后台处理相关的数据,最后回到主队列中来发送你在后台队列中得到的结果。使用这种方式,你不需要自己做任何锁操作,这也就大大减少了犯错误的几率。
[常见的后台实践] (http://objccn.io/issue-2-2/)(这个好!!! CoreData并行编程)
- 后台的 Core Data
- 后台 UI 代码
- 后台绘制
- 异步网络请求处理
- 进阶:后台文件 I/O
- 引用原文:要修正这个问题,我们需要做一些无论如何都应该做的事情:批量保存。在导入较大的数据时,我们需要定期保存,逐渐导入,否则内存很可能就会被耗光,性能一般也会更坏。而且,定期保存也可以分散主线程在更新 table view 时的工作压力。 合理的保存的次数可以通过试错得到。保存太频繁的话,可能会在 I/O 操作上花太多时间;保存次数太少的话,应用会变得无响应。在经过一些尝试后,我们设定每 250 次导入就保存一次。改进后,导入过程变得很平滑,它可以适时更新 table view,也没有阻塞主 context 太久。
- 大文件:惰性读取 (lazily read) 的方式逐行读入
本文最后的示例将使用输入流的方式来实现这个特性,在 StackOverflow 上 Dave DeLong 也提供了一段非常好的示例代码来说明这个问题。
- 最后,最近对于 child contexts 有很多争议。我们的建议是不要在后台操作中使用它。如果你以主 context 的 child 的方式创建了一个后台 context 的话,保存这个后台 context 将阻塞主线程。而要是将主 context 作为后台 context 的 child 的话,实际上和与创建两个传统的独立 contexts 来说是没有区别的。因为你仍然需要手动将后台的改变合并回主 context 中去。设置一个 persistent store coordinator 和两个独立的 contexts 被证明了是在后台处理 Core Data 的好方法。除非你有足够好的理由,否则在处理时你应该坚持使用这种方式。
- 目标队列
- 目标队列你能够为你创建的任何一个队列设置一个目标队列。这会是很强大的,并且有助于调试。
为一个类创建它自己的队列而不是使用全局的队列被普遍认为是一种好的风格。这种方式下,你可以设置队列的名字,这让调试变得轻松许多—— Xcode 可以让你在 Debug Navigator 中看到所有的队列名字,如果你直接使用 lldb。(lldb) thread list 命令将会在控制台打印出所有队列的名字。一旦你使用大量的异步内容,这会是非常有用的帮助。
如何写出好的异步 API
如果你正在给设计一个给别人(或者是给自己)使用的 API,你需要记住几种好的实践。(打开网址看详情)
- Wikipedia:
Concurrency (computer science), a property of systems in which several processes are executing at the same time. — 即:“同时发生”。
Process (computing), a computer program, or running a program concurrently with other programs.
NSOperationQueue and concurrent vs non-concurrent
In the context of an NSOperation object, the terms concurrent and non-concurrent do not necessarily refer to the side-by-side execution of threads. Instead, a non-concurrent operation is one that executes using the environment that is provided for it while a concurrent operation is responsible for setting up its own execution environment.
A “non-concurrent” operation requires a separate thread in order to execute concurrently. NSOperationQueue is responsible for providing this thread. In other words, a non-concurrent operation depends on NSOperationQueue to make it a concurrent operation.
A “concurrent” operation is concurrent on its own; it doesn’t need NSOperationQueue to create a thread for it. An example would be an operation that uses asynchronous file IO.
If you want two or more operations to execute serially you need to use dependencies.
If you want an operation to block the main thread then don’t use NSOperationQueue; just run each operation one after the other on the main thread.
To manually set maximum of concurrent operations, use method on operationQueue setMaxConcurrentOperationCount:
NSOperationQueue Class Reference: Operation queues usually provide the threads used to run their operations. Operation queues use the libdispatch library (also known as Grand Central Dispatch) to initiate the execution of their operations. As a result, operations are always executed on a separate thread, regardless of whether they are designated as asynchronous or synchronous operations.
It is safe to use a single NSOperationQueue object from multiple threads without creating additional locks to synchronize access to that object.
-(void)addDependency:(NSOperation *)operation
The receiver is not considered ready to execute until all of its dependent operations have finished executing. If the receiver is already executing its task, adding dependencies has no practical effect. This method may change the isReady and dependencies properties of the receiver.
It is a programmer error to create any circular dependencies among a set of operations. Doing so can cause a deadlock among the operations and may freeze your program.