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.
與線程相關的程序,你應該明智的使用他們在絕對必要的時候。