我们真的需要多线程吗?

Before you even consider redesigning your code to support concurrency, you should ask yourself whether doing so is necessary. Concurrency can improve the responsiveness of your code by ensuring that your main thread is free to respond to user events. It can even improve the efficiency of your code by  more cores to do more work in the same amount of time. However, it also adds overhead and increases the overall complexity of your code, making it harder to write and debug your code.

在你甚至考虑重新设计你的代码支持并发前,你应该问问自己这样做有必要吗?并发可以改善你代码的响应能力通过保证你的主线程能攻自由响应用户事件。它甚至可以利用通过更多的内核在相同的时间内进行更多的工作来提高代码的效率。然而,它也增加了间接成本,增加了你代码整体的复杂性。使他更加难于书写,并且难以调试。

Because it adds complexity, concurrency is not a feature that you can graft onto an application at the end of your product cycle. Doing it right requires careful consideration of the tasks your application performs and the data structures used to perform those tasks. Done incorrectly, you might find your code runs slower than before and is less responsive to the user. Therefore, it is worthwhile to take some time at the beginning of your design cycle to set some goals and to think about the approach you need to take.

由于增加复杂性的原因,并发性并不是你在程序结束时你就可以移植到程序的特性。正确的使用它需要自习考虑程序运行的任务和运执行这些任务的数据结构。如果用的不正确,你可能发现你的代码跑的比以前更慢了,交互响应更慢了。因此,它是必要的在程序开发周期开始之前需要花费一些时间来设置一些目标,需要采取的方法。

Every application has different requirements and a different set of tasks that it performs. It is impossible for a document to tell you exactly how to design your application and its associated tasks. However, the following sections try to provide some guidance to help you make good choices during the design process.

每个程序都有不同的需求,需要执行不同的任务。告诉你怎样设计你的程序以及执行一系列不同的任务是不可能的。然而下边几部分尝试提供一些引导帮助你在设计过程中做出更好的选择。

Define Your Application’s Expected Behavior

定义程序的预期行为

Before you even think about adding concurrency to your application, you should always start by defining what you deem to be the correct behavior of your application. Understanding your application’s expected behavior gives you a way to validate your design later. It should also give you some idea of the expected performance benefits you might receive by introducing concurrency.

在你添加并发性之前,你应该通过定义你需要怎样正确的行为来开始。理解了你的程序的预期行为,会给你一种正确的方式来之后验证你的设计。亦应该思考通过引入并发会使你获得的预期性能优势。

The first thing you should do is enumerate the tasks your application performs and the objects or data structures associated with each task. Initially, you might want to start with tasks that are performed when the user selects a menu item or clicks a button. These tasks offer discrete behavior and have a well defined start and end point. You should also enumerate other types of tasks your application may perform without user interaction, such as timer-based tasks.

你应该做的第一件事是枚举你应用程序中的任务,以及和每个任务相关的对象和数据结构。最初,你可能想要以用户选择菜单或点击事件被执行的任务为开始。这些任务提供的行为比较离散,并且都定义一个好的起点和终点。处理用户交互,你还应该枚举程序中的其他任务,例如基于定时器的任务。

After you have your list of high-level tasks, start breaking each task down further into the set of steps that must be taken to complete the task successfully. At this level, you should be primarily concerned with the modifications you need to make to any data structures and objects and how those modifications affect your application’s overall state. You should also note any dependencies between objects and data structures as well. For example, if a task involves making the same change to an array of objects, it is worth noting whether the changes to one object affect any other objects. If the objects can be modified independently of each other, that might be a place where you could make those modifications concurrently.

当您有高级任务列表后,开始将每个任务分解为可成功完成的任务。在这个层次,你应该首先去关注对数据结构和对象的修改,以及这些修改对整个程序的影响。你还应该关注对象和数据结构的依赖关系。例如,如果一个任务涉及到对一个数组的相同的修改。指的关注的是是否一个对象的修改会对其他对象产生影响。如果对象可以被独立的修改,那可能是你可以同时修改的地方。

Factor Out Executable Units of Work

可执行的工作单元

From your understanding of your application’s tasks, you should already be able to identify places where your code might benefit from concurrency. If changing the order of one or more steps in a task changes the results, you probably need to continue performing those steps serially. If changing the order has no effect on the output, though, you should consider performing those steps concurrently. In both cases, you define the executable unit of work that represents the step or steps to be performed. This unit of work then becomes what you encapsulate using either a block or an operation object and dispatch to the appropriate queue.

从你你接了你的程序任务开始,你已经有能力判断你的哪部分代码会从并发中受益。如果改变一个任务的一个或多个步骤会使结果改变。你可能需要串行执行执行这些任务。如果改变顺序不会影响输出结果,那么,你应该考虑同时执行这些步骤。在这两种情况,你都可以定义可执行的单元,代表这些步骤或者执行步骤。这些单元用block或operation对象封装,分配到合适的队列。

For each executable unit of work you identify, do not worry too much about the amount of work being performed, at least initially. Although there is always a cost to spinning up a thread, one of the advantages of dispatch queues and operation queues is that in many cases those costs are much smaller than they are for traditional threads. Thus, it is possible for you to execute smaller units of work more efficiently using queues than you could using threads. Of course, you should always measure your actual performance and adjust the size of your tasks as needed, but initially, no task should be considered too small.

对于你定义的每个执行单元,在初始阶段不要考虑执行任务的工作量。尽管切换线程有成本。但是diapatch队列和queue队列优势在于,在大多数情况下,这些成本要小于传统的thread。因此,对于你来说使用queue比threads执行小单元的work,成本效率要高的多。当然,你应该根据需要来调整所需任务的大小。但是在开始时,任务不应该考虑的太小。

Identify the Queues You Need

定义您需要的队列

Now that your tasks are broken up into distinct units of work and encapsulated usingblock objects or operation objects, you need to define the queues you are going to use to execute that code. For a given task, examine the blocks or operation objects you created and the order in which they must be executed to perform the task correctly.

既然你的任务已经分解到小单元,并且用block和operation进行了封装,你需要定义你需要执行的queue来执行代码。对于一个给定的任务,检查你创建的bloc和operation,以及你要执行的顺序,正确的执行这些任务。

If you implemented your tasks using blocks, you can add your blocks to either a serial or concurrent dispatch queue. If a specific order is required, you would always add your blocks to a serial dispatch queue. If a specific order is not required, you can add the blocks to a concurrent dispatch queue or add them to several different dispatch queues, depending on your needs.

如果你用block实现了你要做的额任务,你可以把它放入串行或者并发队列中。如果顺序是不必要的,你可以添加任务到并发队列或者添加到不同的队列中,这看你的需求。

If you implemented your tasks using operation objects, the choice of queue is often less interesting than the configuration of your objects. To perform operation objects serially, you must configure dependencies between the related objects. Dependencies prevent one operation from executing until the objects on which it depends have finished their work.

如果你使用operation实现了任务,queue的选择没有而配置有趣。为了顺序执行任务,你必须配置相关对象的依赖可以对象执行知道依赖完成。

Tips for Improving Efficiency

提高效率的提示

In addition to simply factoring your code into smaller tasks and adding them to a queue, there are other ways to improve the overall efficiency of your code using queues:

除了分解为小任务然后添加到队列,还有其他的方式改善queue的整体效率。

1.Consider computing values directly within your task if memory usage is a factor.If your application is already memory bound, computing values directly now may be faster than loading cached values from main memory. Computing values directly uses the registers and caches of the given processor core, which are much faster than main memory. Of course, you should only do this if testing indicates this is a performance win.

如果内存是一个因素,可以考虑直接计算值。如果你的内存已经紧张了,直接计算值比加载内存的缓存嘟嘟更快。

Identify serial tasks early and do what you can to make them more concurrent.If a task must be executed serially because it relies on some shared resource, consider changing your architecture to remove that shared resource. You might consider making copies of the resource for each client that needs one or eliminate the resource altogether.

早起的串行任务,可以试着让它们更加并发的执行。如果任务必须串行执行,由于它依赖于一些共享资源。可以考虑改变你的架构,移除共享资源。你可以考虑copy一份资源或者完全消除资源。

Avoid using locks.The support provided by dispatch queues and operation queues makes locks unnecessary in most situations. Instead of using locks to protect some shared resource, designate a serial queue (or use operation object dependencies) to execute tasks in the correct order.

避免使用锁,dispatch队列和operation队列支持在大多数情况下不需要使用锁。不要使用锁来保护共享资源,而是涉及一个串行队列或者利用依赖按照正确的顺序来执行任务。

Rely on the system frameworks whenever possible.The best way to achieve concurrency is to take advantage of the built-in concurrency provided by the system frameworks. Many frameworks use threads and other technologies internally to implement concurrent behaviors. When defining your tasks, look to see if an existing framework defines a function or method that does exactly what you want and does so concurrently. Using that API may save you effort and is more likely to give you the maximum concurrency possible.

尽可能的利用系统框架。最好的方式实现并发是充分利用系统框架。许多框架使用thread和其他内部技术来实现并发行为。当定义线程的时候,看看框架定义的函数或者方法可以实现你想要做的并发。利用API可以洁身你的力气,能达到最大的并发。

Performance Implications

性能意义

Operation queues, dispatch queues, and dispatch sources are provided to make it easier for you to execute more code concurrently. However, these technologies do not guarantee improvements to the efficiency or responsiveness in your application. It is still your responsibility to use queues in a manner that is both effective for your needs and does not impose an undue burden on your application’s other resources. For example, although you could create 10,000 operation objects and submit them to an operation queue, doing so would cause your application to allocate a potentially nontrivial amount of memory, which could lead to paging and decreased performance.

operation队列,dispatch队列和调度源使你执行异步代码更加的方便。然而,这些技术并不能保证一定高效,或者在你的程序中响应更快。你有责任以一种对于你的需求使用一种更加高效的方式,不要给你程序的其他资源造负担。例如:尽管你可以创建1000个operation,然后提交他们到队列。这样做将会引起你的应用开辟大量内存,导致性能下降。

Before introducing any amount of concurrency to your code—whether using queues or threads—you should always gather a set of baseline metrics that reflect your application’s current performance. After introducing your changes, you should then gather additional metrics and compare them to your baseline to see if your application’s overall efficiency has improved. If the introduction of concurrency makes your application less efficient or responsive, you should use the available performance tools to check for the potential causes.

在引入并发之前 ,你应该引入一些指标来检测性能,如果引入并发之后应用程序的性能下降了。你应该应用性能工具来检测潜在原因。

Concurrency and Other Technologies

并发和其他技术

Factoring your code into modular tasks is the best way to try and improve the amount of concurrency in your application. However, this design approach may not satisfy the needs of every application in every case. Depending on your tasks, there might be other options that can offer additional improvements in your application’s overall concurrency. This section outlines some of the other technologies to consider using as part of your design.

将代码分解为模块化任务是尝试改善程序并发数量的最佳方式。然而这种方式可能无法满足所有程序所有情况的需要。看你的任务,你程序的整体性能可能有其他选项来改善并发性能。本节是一些其他技术可以考虑作为你技术的一部分。

OpenCL and Concurrency

OpenCL 和并发

In OS X, the Open Computing Language (OpenCL)is a standards-based technology for performing general-purpose computations on a computer’s graphics processor. OpenCL is a good technology to use if you have a well-defined set of computations that you want to apply to large data sets. For example, you might use OpenCL to perform filter computations on the pixels of an image or use it to perform complex math calculations on several values at once. In other words, OpenCL is geared more toward problem sets whose data can be operated on in parallel.

在OS X中OpenCL是一种基于标准的技术,用于在计算机的图形处理器上执行通用计算。OpenCL是一门技术用来大数据的计算。例如,以可以使用OpenCL对图片进行过滤计算或者记性复杂的数据计算。换句话说,OpenCL更适合于数据并行操作的问题。

Although OpenCL is good for performing massively data-parallel operations, it is not suitable for more general-purpose calculations. There is a nontrivial amount of effort required to prepare and transfer both the data and the required work kernel to a graphics card so that it can be operated on by a GPU. Similarly, there is a nontrivial amount of effort required to retrieve any results generated by OpenCL. As a result, any tasks that interact with the system are generally not recommended for use with OpenCL. For example, you would not use OpenCL to process data from files or network streams. Instead, the work you perform using OpenCL must be much more self-contained so that it can be transferred to the graphics processor and computed independently.

尽管OpenCL更适合大数据量的并行计算,不适合于通用计算。为方便GPU操作,需要大量的准备。然后把数据给GPU使用。同样,检索OpenCL的任何结果都需要大量的努力。因此,涉及到与系统交互的任何结果一般都不推荐使用OpenCL,例如,你将不要用OpenCL来处理从网络或者文件获取的数据。相反,你用OpenCL运行的work必须进行自包含,以便其传输用来图像处理以及独立计算。

When to Use Threads

什么税后使用线程呢?

Although operation queues and dispatch queues are the preferred way to perform tasks concurrently, they are not a panacea. Depending on your application, there may still be times when you need to create custom threads. If you do create custom threads, you should strive to create as few threads as possible yourself and you should use those threads only for specific tasks that cannot be implemented any other way.

尽管operation队列和dispatch queue是并发执行任务的首选方式,但他们并不是灵丹妙药。根据您的应用程序,有时候还是需要创建自定义线程的。如果你一定要创建线程,你应该尽可能的创建少的线程,你用thread应该只有当不能用其他方式实现的时候。

Threads are still a good way to implement code that must run in real time. Dispatch queues make every attempt to run their tasks as fast as possible but they do not address real time constraints. If you need more predictable behavior from code running in the background, threads may still offer a better alternative.

线程依然是实现实时代码的一种很好的方式。dispatch只能尽可能的跑的快,但是却不能解决实时的问题。如果你想要在后台预测实时行为,线程依然是很好的选择。

As with any threaded programming, you should always use threads judiciously and only when absolutely necessary. For more information about thread packages and how you use them, seeThreading Programming Guide.

与线程相关的程序,你应该明智的使用他们在绝对必要的时候。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容