我們真的需要多線程嗎?

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.

在你甚至考慮重新設(shè)計(jì)你的代碼支持并發(fā)前,你應(yīng)該問(wèn)問(wèn)自己這樣做有必要嗎?并發(fā)可以改善你代碼的響應(yīng)能力通過(guò)保證你的主線程能攻自由響應(yīng)用戶事件。它甚至可以利用通過(guò)更多的內(nèi)核在相同的時(shí)間內(nèi)進(jìn)行更多的工作來(lái)提高代碼的效率。然而,它也增加了間接成本,增加了你代碼整體的復(fù)雜性。使他更加難于書(shū)寫(xiě),并且難以調(diào)試。

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.

由于增加復(fù)雜性的原因,并發(fā)性并不是你在程序結(jié)束時(shí)你就可以移植到程序的特性。正確的使用它需要自習(xí)考慮程序運(yùn)行的任務(wù)和運(yùn)執(zhí)行這些任務(wù)的數(shù)據(jù)結(jié)構(gòu)。如果用的不正確,你可能發(fā)現(xiàn)你的代碼跑的比以前更慢了,交互響應(yīng)更慢了。因此,它是必要的在程序開(kāi)發(fā)周期開(kāi)始之前需要花費(fèi)一些時(shí)間來(lái)設(shè)置一些目標(biāo),需要采取的方法。

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.

每個(gè)程序都有不同的需求,需要執(zhí)行不同的任務(wù)。告訴你怎樣設(shè)計(jì)你的程序以及執(zhí)行一系列不同的任務(wù)是不可能的。然而下邊幾部分嘗試提供一些引導(dǎo)幫助你在設(shè)計(jì)過(guò)程中做出更好的選擇。

Define Your Application’s Expected Behavior

定義程序的預(yù)期行為

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.

在你添加并發(fā)性之前,你應(yīng)該通過(guò)定義你需要怎樣正確的行為來(lái)開(kāi)始。理解了你的程序的預(yù)期行為,會(huì)給你一種正確的方式來(lái)之后驗(yàn)證你的設(shè)計(jì)。亦應(yīng)該思考通過(guò)引入并發(fā)會(huì)使你獲得的預(yù)期性能優(yōu)勢(shì)。

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.

你應(yīng)該做的第一件事是枚舉你應(yīng)用程序中的任務(wù),以及和每個(gè)任務(wù)相關(guān)的對(duì)象和數(shù)據(jù)結(jié)構(gòu)。最初,你可能想要以用戶選擇菜單或點(diǎn)擊事件被執(zhí)行的任務(wù)為開(kāi)始。這些任務(wù)提供的行為比較離散,并且都定義一個(gè)好的起點(diǎn)和終點(diǎn)。處理用戶交互,你還應(yīng)該枚舉程序中的其他任務(wù),例如基于定時(shí)器的任務(wù)。

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.

當(dāng)您有高級(jí)任務(wù)列表后,開(kāi)始將每個(gè)任務(wù)分解為可成功完成的任務(wù)。在這個(gè)層次,你應(yīng)該首先去關(guān)注對(duì)數(shù)據(jù)結(jié)構(gòu)和對(duì)象的修改,以及這些修改對(duì)整個(gè)程序的影響。你還應(yīng)該關(guān)注對(duì)象和數(shù)據(jù)結(jié)構(gòu)的依賴關(guān)系。例如,如果一個(gè)任務(wù)涉及到對(duì)一個(gè)數(shù)組的相同的修改。指的關(guān)注的是是否一個(gè)對(duì)象的修改會(huì)對(duì)其他對(duì)象產(chǎn)生影響。如果對(duì)象可以被獨(dú)立的修改,那可能是你可以同時(shí)修改的地方。

Factor Out Executable Units of Work

可執(zhí)行的工作單元

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.

從你你接了你的程序任務(wù)開(kāi)始,你已經(jīng)有能力判斷你的哪部分代碼會(huì)從并發(fā)中受益。如果改變一個(gè)任務(wù)的一個(gè)或多個(gè)步驟會(huì)使結(jié)果改變。你可能需要串行執(zhí)行執(zhí)行這些任務(wù)。如果改變順序不會(huì)影響輸出結(jié)果,那么,你應(yīng)該考慮同時(shí)執(zhí)行這些步驟。在這兩種情況,你都可以定義可執(zhí)行的單元,代表這些步驟或者執(zhí)行步驟。這些單元用block或operation對(duì)象封裝,分配到合適的隊(duì)列。

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.

對(duì)于你定義的每個(gè)執(zhí)行單元,在初始階段不要考慮執(zhí)行任務(wù)的工作量。盡管切換線程有成本。但是diapatch隊(duì)列和queue隊(duì)列優(yōu)勢(shì)在于,在大多數(shù)情況下,這些成本要小于傳統(tǒng)的thread。因此,對(duì)于你來(lái)說(shuō)使用queue比threads執(zhí)行小單元的work,成本效率要高的多。當(dāng)然,你應(yīng)該根據(jù)需要來(lái)調(diào)整所需任務(wù)的大小。但是在開(kāi)始時(shí),任務(wù)不應(yīng)該考慮的太小。

Identify the Queues You Need

定義您需要的隊(duì)列

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.

既然你的任務(wù)已經(jīng)分解到小單元,并且用block和operation進(jìn)行了封裝,你需要定義你需要執(zhí)行的queue來(lái)執(zhí)行代碼。對(duì)于一個(gè)給定的任務(wù),檢查你創(chuàng)建的bloc和operation,以及你要執(zhí)行的順序,正確的執(zhí)行這些任務(wù)。

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實(shí)現(xiàn)了你要做的額任務(wù),你可以把它放入串行或者并發(fā)隊(duì)列中。如果順序是不必要的,你可以添加任務(wù)到并發(fā)隊(duì)列或者添加到不同的隊(duì)列中,這看你的需求。

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實(shí)現(xiàn)了任務(wù),queue的選擇沒(méi)有而配置有趣。為了順序執(zhí)行任務(wù),你必須配置相關(guān)對(duì)象的依賴可以對(duì)象執(zhí)行知道依賴完成。

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:

除了分解為小任務(wù)然后添加到隊(duì)列,還有其他的方式改善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.

如果內(nèi)存是一個(gè)因素,可以考慮直接計(jì)算值。如果你的內(nèi)存已經(jīng)緊張了,直接計(jì)算值比加載內(nèi)存的緩存嘟嘟更快。

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.

早起的串行任務(wù),可以試著讓它們更加并發(fā)的執(zhí)行。如果任務(wù)必須串行執(zhí)行,由于它依賴于一些共享資源??梢钥紤]改變你的架構(gòu),移除共享資源。你可以考慮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隊(duì)列和operation隊(duì)列支持在大多數(shù)情況下不需要使用鎖。不要使用鎖來(lái)保護(hù)共享資源,而是涉及一個(gè)串行隊(duì)列或者利用依賴按照正確的順序來(lái)執(zhí)行任務(wù)。

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.

盡可能的利用系統(tǒng)框架。最好的方式實(shí)現(xiàn)并發(fā)是充分利用系統(tǒng)框架。許多框架使用thread和其他內(nèi)部技術(shù)來(lái)實(shí)現(xiàn)并發(fā)行為。當(dāng)定義線程的時(shí)候,看看框架定義的函數(shù)或者方法可以實(shí)現(xiàn)你想要做的并發(fā)。利用API可以潔身你的力氣,能達(dá)到最大的并發(fā)。

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隊(duì)列,dispatch隊(duì)列和調(diào)度源使你執(zhí)行異步代碼更加的方便。然而,這些技術(shù)并不能保證一定高效,或者在你的程序中響應(yīng)更快。你有責(zé)任以一種對(duì)于你的需求使用一種更加高效的方式,不要給你程序的其他資源造負(fù)擔(dān)。例如:盡管你可以創(chuàng)建1000個(gè)operation,然后提交他們到隊(duì)列。這樣做將會(huì)引起你的應(yīng)用開(kāi)辟大量?jī)?nèi)存,導(dǎo)致性能下降。

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.

在引入并發(fā)之前 ,你應(yīng)該引入一些指標(biāo)來(lái)檢測(cè)性能,如果引入并發(fā)之后應(yīng)用程序的性能下降了。你應(yīng)該應(yīng)用性能工具來(lái)檢測(cè)潛在原因。

Concurrency and Other Technologies

并發(fā)和其他技術(shù)

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.

將代碼分解為模塊化任務(wù)是嘗試改善程序并發(fā)數(shù)量的最佳方式。然而這種方式可能無(wú)法滿足所有程序所有情況的需要??茨愕娜蝿?wù),你程序的整體性能可能有其他選項(xiàng)來(lái)改善并發(fā)性能。本節(jié)是一些其他技術(shù)可以考慮作為你技術(shù)的一部分。

OpenCL and Concurrency

OpenCL 和并發(fā)

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是一種基于標(biāo)準(zhǔn)的技術(shù),用于在計(jì)算機(jī)的圖形處理器上執(zhí)行通用計(jì)算。OpenCL是一門(mén)技術(shù)用來(lái)大數(shù)據(jù)的計(jì)算。例如,以可以使用OpenCL對(duì)圖片進(jìn)行過(guò)濾計(jì)算或者記性復(fù)雜的數(shù)據(jù)計(jì)算。換句話說(shuō),OpenCL更適合于數(shù)據(jù)并行操作的問(wèn)題。

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更適合大數(shù)據(jù)量的并行計(jì)算,不適合于通用計(jì)算。為方便GPU操作,需要大量的準(zhǔn)備。然后把數(shù)據(jù)給GPU使用。同樣,檢索OpenCL的任何結(jié)果都需要大量的努力。因此,涉及到與系統(tǒng)交互的任何結(jié)果一般都不推薦使用OpenCL,例如,你將不要用OpenCL來(lái)處理從網(wǎng)絡(luò)或者文件獲取的數(shù)據(jù)。相反,你用OpenCL運(yùn)行的work必須進(jìn)行自包含,以便其傳輸用來(lái)圖像處理以及獨(dú)立計(jì)算。

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隊(duì)列和dispatch queue是并發(fā)執(zhí)行任務(wù)的首選方式,但他們并不是靈丹妙藥。根據(jù)您的應(yīng)用程序,有時(shí)候還是需要?jiǎng)?chuàng)建自定義線程的。如果你一定要?jiǎng)?chuàng)建線程,你應(yīng)該盡可能的創(chuàng)建少的線程,你用thread應(yīng)該只有當(dāng)不能用其他方式實(shí)現(xiàn)的時(shí)候。

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.

線程依然是實(shí)現(xiàn)實(shí)時(shí)代碼的一種很好的方式。dispatch只能盡可能的跑的快,但是卻不能解決實(shí)時(shí)的問(wèn)題。如果你想要在后臺(tái)預(yù)測(cè)實(shí)時(shí)行為,線程依然是很好的選擇。

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.

與線程相關(guān)的程序,你應(yīng)該明智的使用他們?cè)诮^對(duì)必要的時(shí)候。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容