文檔地址
介紹
線程是幾個(gè)能使單個(gè)程序同時(shí)并發(fā)執(zhí)行多個(gè)代碼路徑成為可能的技術(shù)之一断序。即使像operation objects和Grand Central Dispatch (GCD) 這些新技術(shù)為實(shí)現(xiàn)同步并發(fā)提供了更加現(xiàn)代化和高效的基礎(chǔ)框架余素,OS X 和 iOS依然提供接口來(lái)創(chuàng)建和管理線程晤斩。
關(guān)于線程編程
多年來(lái)莺匠,極限計(jì)算機(jī)的性能在計(jì)算機(jī)核心上主要受制于單個(gè)微型處理器的速度。隨著單核處理器達(dá)到了實(shí)際的限制姨蝴,芯片制造商進(jìn)而轉(zhuǎn)化為多核設(shè)計(jì)焊傅,使計(jì)算機(jī)有機(jī)會(huì)同時(shí)執(zhí)行多個(gè)任務(wù)。盡管 OS X 使用這些核心執(zhí)行系統(tǒng)相關(guān)的任務(wù)摊唇,我們自己的程序依然可以通過(guò)線程使用它們咐蝇。
什么是線程
線程是程序內(nèi)部實(shí)現(xiàn)多條執(zhí)行路徑的一種相對(duì)輕量級(jí)的方式。在系統(tǒng)層面巷查,程序并行運(yùn)行有序,系統(tǒng)根據(jù)程序的需要分配給每個(gè)程序執(zhí)行時(shí)間。然而岛请,在每個(gè)程序內(nèi)部旭寿,存在一個(gè)或多個(gè)執(zhí)行線程,用于同時(shí)或者幾乎同時(shí)的方式執(zhí)行不同任務(wù)崇败。系統(tǒng)實(shí)際管理這些執(zhí)行線程盅称,安排它們運(yùn)行在空閑的核心上,并根據(jù)需要搶先中斷它們?nèi)ピ试S其他線程運(yùn)行后室。
從技術(shù)角度來(lái)看缩膝,線程是一個(gè)內(nèi)核級(jí)和應(yīng)用級(jí)數(shù)據(jù)結(jié)構(gòu)需要去管理執(zhí)行代碼的組合。內(nèi)核級(jí)結(jié)構(gòu)協(xié)調(diào)派發(fā)事件到線程和優(yōu)先安排線程到一個(gè)有用的核心上岸霹。應(yīng)用級(jí)結(jié)構(gòu)包括存儲(chǔ)函數(shù)調(diào)用的調(diào)用棧以及應(yīng)用程序所需管理和操作的線程數(shù)據(jù)與狀態(tài)的結(jié)構(gòu)疾层。
在非并發(fā)的應(yīng)用程序中,它們只有一個(gè)執(zhí)行線程贡避。線程以應(yīng)用程序的主例程以及逐個(gè)到實(shí)現(xiàn)應(yīng)用程序整體行為的不同方法和函數(shù)的分支開始和結(jié)束云芦。相比之下俯逾,一個(gè)支持多線程的應(yīng)用程序以一個(gè)線程開始,然后根據(jù)需要添加更多的執(zhí)行路徑舅逸。每個(gè)新路徑有自己的自定義開始例程運(yùn)行獨(dú)立于應(yīng)用程序主例程的代碼桌肴。多線程應(yīng)用程序提供兩個(gè)非常重要的潛在優(yōu)勢(shì):
- 多線程可以提高應(yīng)用程序的感知響應(yīng)能力。
- 多線程可以提高應(yīng)用程序在多核系統(tǒng)上的實(shí)時(shí)性能琉历。
如果你的應(yīng)用程序只有一個(gè)線程坠七,那么這個(gè)線程要干所有的事。它需要響應(yīng)事件旗笔,更新應(yīng)用程序窗口彪置,并執(zhí)行實(shí)現(xiàn)應(yīng)用程序行為的所有計(jì)算。問(wèn)題是蝇恶,一個(gè)線程只能同時(shí)干一件事情拳魁。因此當(dāng)一個(gè)計(jì)算需要執(zhí)行很長(zhǎng)一段時(shí)間才能完成的時(shí)候會(huì)發(fā)生什么呢?當(dāng)你的代碼正在忙碌的計(jì)算它所需要的值撮弧,你的應(yīng)用程序就停止了響應(yīng)用戶事件和更新窗口潘懊。如果這種行為經(jīng)行了很長(zhǎng)的時(shí)間,用戶可能認(rèn)為你的應(yīng)用程序掛起了并且會(huì)強(qiáng)制退出贿衍。如果把自定義的計(jì)算移動(dòng)到一個(gè)分開的線程中授舟,則應(yīng)用程序的主線程就有空閑及時(shí)的響應(yīng)用戶交互。
隨著最近多核計(jì)算機(jī)的普及贸辈,線程提供了一種提高某些類型應(yīng)用程序性能的方法释树。執(zhí)行不同任務(wù)的線程可以在不同的處理器內(nèi)核上同時(shí)執(zhí)行,從而使應(yīng)用程序可以在給定的時(shí)間內(nèi)增加它的工作量擎淤。
當(dāng)然奢啥,線程并不是解決應(yīng)用程序性能問(wèn)題的靈丹妙藥。隨著線程提供的好處帶來(lái)了潛在的問(wèn)題嘴拢。在應(yīng)用程序中具有多個(gè)執(zhí)行路徑會(huì)給代碼增加相當(dāng)大的復(fù)雜性扫尺。每個(gè)線程都必須與其他線程協(xié)調(diào)其操作,以防止它破壞應(yīng)用程序的狀態(tài)信息炊汤。由于單個(gè)應(yīng)用程序中的線程共享相同的內(nèi)存空間正驻,因此它們可以訪問(wèn)所有相同的數(shù)據(jù)結(jié)構(gòu)。如果兩個(gè)線程試圖同時(shí)操作相同的數(shù)據(jù)結(jié)構(gòu)抢腐,一個(gè)線程可能覆蓋其他線程的更改這意味著破壞了結(jié)果數(shù)據(jù)結(jié)構(gòu)姑曙。即使有適當(dāng)?shù)谋Wo(hù)措施,您仍然需要注意編譯器優(yōu)化迈倍,這些優(yōu)化會(huì)在代碼中引入細(xì)微(而不是那么微妙)的錯(cuò)誤伤靠。
線程術(shù)語(yǔ)
在深入討論線程及其支持技術(shù)之前,有必要定義一些基本術(shù)語(yǔ)。
如果您熟悉UNIX系統(tǒng)宴合,則可能會(huì)發(fā)現(xiàn)本文檔對(duì)“任務(wù)”一詞的使用方式不同焕梅。在UNIX系統(tǒng)上,術(shù)語(yǔ)“任務(wù)”有時(shí)用于指代正在運(yùn)行的進(jìn)程卦洽。
本文件采用以下術(shù)語(yǔ):
- 術(shù)語(yǔ)線程用于指代代碼的單獨(dú)執(zhí)行路徑贞言。
- 術(shù)語(yǔ)進(jìn)程用于指代正在運(yùn)行的可執(zhí)行文件,其可以包含多個(gè)線程阀蒂。
- 術(shù)語(yǔ)任務(wù)用于指代需要執(zhí)行的抽象工作概念该窗。
線程的代替品
自己創(chuàng)建線程的一個(gè)問(wèn)題是它們會(huì)給代碼增加不確定性。線程是一種支持應(yīng)用程序并發(fā)性的相對(duì)低級(jí)且復(fù)雜的方法蚤霞。如果您不完全了解設(shè)計(jì)選擇的含義酗失,則可能很容易遇到同步或計(jì)時(shí)問(wèn)題,其嚴(yán)重性可能從細(xì)微的行為更改到應(yīng)用程序崩潰以及用戶數(shù)據(jù)損壞昧绣。
另一個(gè)需要考慮的因素是你是否需要線程或并發(fā)规肴。線程解決了如何在同一進(jìn)程內(nèi)同時(shí)執(zhí)行多個(gè)代碼路徑的特定問(wèn)題。但是夜畴,在某些情況下拖刃,您所做的工作量并不能保證并發(fā)性。線程在內(nèi)存消耗和CPU時(shí)間方面為您的進(jìn)程帶來(lái)了巨大的開銷斩启。您可能會(huì)發(fā)現(xiàn)此開銷對(duì)于預(yù)期任務(wù)來(lái)說(shuō)太大了,或者其他選項(xiàng)更容易實(shí)現(xiàn)醉锅。
表1-1列出了一些線程的替代方案兔簇。該表包括線程的替換技術(shù)(例如Operation objects和GCD)以及旨在有效使用您已有的單線程的替代方法。
表1-1 線程的替代技術(shù)
技術(shù) | 描述 |
---|---|
Operation objects | 在OS X v10.5中引入的operation object是通常在輔助線程上執(zhí)行的任務(wù)的包裝器硬耍。這個(gè)包裝器隱藏了執(zhí)行任務(wù)的線程管理方面垄琐,讓您可以專注于任務(wù)本身。您通常將這些對(duì)象與操作隊(duì)列對(duì)象結(jié)合使用经柴,該操作隊(duì)列對(duì)象實(shí)際上管理一個(gè)或多個(gè)線程上的操作對(duì)象的執(zhí)行狸窘。有關(guān)如何使用操作對(duì)象的更多信息,請(qǐng)參閱“并發(fā)編程指南”坯认。 |
Grand Central Dispatch (GCD) | 在Mac OS x v10.6中引入翻擒,Grand Central Dispatch是線程的另一種替代方案,可讓您專注于執(zhí)行所需的任務(wù)牛哺,而不是線程管理陋气。使用GCD,您可以定義要執(zhí)行的任務(wù)并將其添加到工作隊(duì)列引润,該隊(duì)列在適當(dāng)?shù)木€程上處理任務(wù)的計(jì)劃巩趁。工作隊(duì)列考慮到可用內(nèi)核的數(shù)量以及當(dāng)前負(fù)載去執(zhí)行你的任務(wù)比起自己使用線程更高效。有關(guān)如何使用GCD和工作隊(duì)列的信息淳附,請(qǐng)參閱“并發(fā)編程指南” |
空閑時(shí)間通知 | 對(duì)于相對(duì)較短且優(yōu)先級(jí)較低的任務(wù)议慰,空閑時(shí)間通知允許您在應(yīng)用程序不忙時(shí)執(zhí)行任務(wù)蠢古。Cocoa使用該NSNotificationQueue對(duì)象提供對(duì)空閑時(shí)間通知的支持。要請(qǐng)求一個(gè)空閑時(shí)間通知别凹,使用該NSPostWhenIdle選項(xiàng)發(fā)送通知到默認(rèn)的NSNotificationQueue對(duì)象草讶。隊(duì)列延遲通知對(duì)象的傳遞,直到運(yùn)行循環(huán)變?yōu)榭臻e番川。有關(guān)更多信息到涂,請(qǐng)參閱通知編程主題。 |
異步函數(shù) | 這些API可能使用系統(tǒng)守護(hù)程序和進(jìn)程或創(chuàng)建自定義線程來(lái)執(zhí)行其任務(wù)并將結(jié)果返回給您颁督。(實(shí)際的實(shí)現(xiàn)是無(wú)關(guān)緊要的践啄,因?yàn)樗c代碼分離。)在設(shè)計(jì)應(yīng)用程序時(shí)沉御,查找提供異步行為的函數(shù)屿讽,并考慮使用它們而不是在自定義線程上使用等效的同步函數(shù)。 |
計(jì)時(shí)器 | 可以在應(yīng)用程序的主線程上使用計(jì)時(shí)器來(lái)執(zhí)行定期任務(wù)吠裆,這些任務(wù)太簡(jiǎn)單而不需要線程伐谈,但仍需要定期維護(hù)。有關(guān)定時(shí)器的信息试疙,請(qǐng)參閱定時(shí)器源诵棵。 |
單獨(dú)的進(jìn)程 | 盡管比線程更加重量級(jí),但在任務(wù)僅與您的應(yīng)用程序相關(guān)的情況下祝旷,創(chuàng)建單獨(dú)的進(jìn)程可能很有用履澳。如果任務(wù)需要大量?jī)?nèi)存或必須使用root權(quán)限執(zhí)行,則可以使用進(jìn)程怀跛。例如距贷,您可以使用64位服務(wù)器進(jìn)程計(jì)算大型數(shù)據(jù)集,而32位應(yīng)用程序?qū)⒔Y(jié)果顯示給用戶吻谋。 |
注意:當(dāng)使用fork函數(shù)啟動(dòng)單獨(dú)的進(jìn)程時(shí)忠蝗,你必須總是用一個(gè)exec或者一個(gè)相似的函數(shù)調(diào)用跟隨fork的調(diào)用。依賴于Core Foundation漓拾,Cocoa或Core Data框架(顯式或隱式)的應(yīng)用程序必須對(duì)exec函數(shù)進(jìn)行后續(xù)調(diào)用阁最,否則這些框架可能表現(xiàn)不正常。
線程支持
如果您有現(xiàn)有使用線程的代碼骇两,OS X和iOS提供了幾種在應(yīng)用程序中創(chuàng)建線程的技術(shù)闽撤。此外,兩個(gè)系統(tǒng)還為管理和同步需要在這些線程上完成的工作提供支持脯颜。以下部分描述了在OS X和iOS中使用線程時(shí)需要注意的一些關(guān)鍵技術(shù)哟旗。
線程包
雖然線程的底層實(shí)現(xiàn)機(jī)制是Mach線程,但很少(如果有的話)使用Mach級(jí)別的線程。相反闸餐,您通常使用更方便的POSIX API或其衍生產(chǎn)品之一饱亮。然而,Mach實(shí)現(xiàn)確實(shí)提供了所有線程的基本功能舍沙,包括搶先執(zhí)行模型和調(diào)度線程的能力近上,因此它們彼此獨(dú)立。
清單2-2列出了可以在應(yīng)用程序中使用的線程技術(shù)拂铡。
表1-2 線程技術(shù)
技術(shù) | 描述 |
---|---|
Cocoa線程 | Cocoa使用NSThread類實(shí)現(xiàn)線程壹无。Cocoa還提供NSObject了生成新線程和在已經(jīng)運(yùn)行的線程上執(zhí)行代碼的方法。有關(guān)更多信息感帅,請(qǐng)參閱使用NSThread和使用NSObject生成線程斗锭。 |
POSIX線程 | POSIX線程提供了一個(gè)用于創(chuàng)建線程的基于C的接口。如果您沒(méi)有編寫Cocoa應(yīng)用程序失球,這是創(chuàng)建線程的最佳選擇岖是。POSIX接口使用起來(lái)相對(duì)簡(jiǎn)單,并為配置線程提供了充分的靈活性实苞。有關(guān)更多信息豺撑,請(qǐng)參閱使用POSIX線程 |
多處理服務(wù) | 多處理服務(wù)是從舊版Mac OS轉(zhuǎn)換的應(yīng)用程序使用的基于C的傳統(tǒng)接口。此技術(shù)僅適用于OS X黔牵,任何新開發(fā)都應(yīng)避免使用聪轿。相反,您應(yīng)該使用NSThread類或POSIX線程猾浦。如果需要有關(guān)此技術(shù)的更多信息陆错,請(qǐng)參閱“ 多處理服務(wù)編程指南”。 |
在應(yīng)用程序級(jí)別跃巡,所有線程的行為方式與其他平臺(tái)上的行為基本相同危号。啟動(dòng)線程后牧愁,線程以三種主要狀態(tài)之一運(yùn)行:running素邪,ready或blocked。如果一個(gè)線程當(dāng)前沒(méi)有運(yùn)行猪半,它將被阻塞并等待輸入兔朦,或者它已準(zhǔn)備好運(yùn)行但尚未安排執(zhí)行此操作。線程繼續(xù)在這些狀態(tài)之間來(lái)回移動(dòng)磨确,直到它最終退出并移動(dòng)到終止?fàn)顟B(tài)沽甥。
創(chuàng)建新線程時(shí),必須為該線程指定入口點(diǎn)函數(shù)(或Cocoa線程的入口點(diǎn)方法)乏奥。此入口點(diǎn)函數(shù)構(gòu)成您要在線程上運(yùn)行的代碼摆舟。當(dāng)函數(shù)返回時(shí),或者顯式終止線程時(shí),線程將永久停止并由系統(tǒng)回收恨诱。由于線程在內(nèi)存和時(shí)間方面的創(chuàng)建成本相對(duì)較高媳瞪,因此建議您的入口點(diǎn)函數(shù)執(zhí)行大量工作或設(shè)置運(yùn)行循環(huán)以允許執(zhí)行重復(fù)工作。
有關(guān)可用線程技術(shù)及其使用方法的更多信息照宝,請(qǐng)參閱線程管理蛇受。
Run Loops
運(yùn)行循環(huán)是一個(gè)基礎(chǔ)結(jié)構(gòu),用于管理在線程上異步到達(dá)的事件厕鹃。運(yùn)行循環(huán)通過(guò)監(jiān)視線程的一個(gè)或多個(gè)事件源來(lái)工作兢仰。當(dāng)事件到達(dá)時(shí),系統(tǒng)喚醒線程并將事件調(diào)度到運(yùn)行循環(huán)剂碴,然后運(yùn)行循環(huán)將它們分派給您指定的處理程序把将。如果沒(méi)有事件存在和準(zhǔn)備需處理,則運(yùn)行循環(huán)使線程進(jìn)入休眠狀態(tài)汗茄。
您不需要對(duì)您創(chuàng)建的任何線程使用運(yùn)行循環(huán)秸弛,但這樣做可以為用戶提供更好的體驗(yàn)。運(yùn)行循環(huán)可以創(chuàng)建使用最少量資源的長(zhǎng)期線程洪碳。因?yàn)檫\(yùn)行循環(huán)在沒(méi)有任何操作時(shí)將其線程置于休眠狀態(tài)递览,所以它消除了輪詢的需要,這會(huì)浪費(fèi)CPU周期并阻止處理器本身休眠并節(jié)省電力瞳腌。
要配置運(yùn)行循環(huán)绞铃,您所要做的就是啟動(dòng)線程,獲取對(duì)運(yùn)行循環(huán)對(duì)象的引用嫂侍,安裝事件處理程序儿捧,并告訴運(yùn)行循環(huán)運(yùn)行。OS X提供的基礎(chǔ)結(jié)構(gòu)會(huì)自動(dòng)為您處理主線程的運(yùn)行循環(huán)的配置挑宠。但是菲盾,如果您計(jì)劃創(chuàng)建長(zhǎng)期存在的輔助線程,則必須自己為這些線程配置運(yùn)行循環(huán)各淀。
中提供了有關(guān)運(yùn)行循環(huán)以及如何使用它們的例子詳細(xì)運(yùn)行循環(huán)懒鉴。
同步工具
線程編程的一個(gè)危險(xiǎn)是多線程之間的資源爭(zhēng)用。如果多個(gè)線程嘗試同時(shí)使用或修改同一資源碎浇,則可能會(huì)出現(xiàn)問(wèn)題临谱。緩解該問(wèn)題的一種方法是完全消除共享資源念秧,并確保每個(gè)線程都有自己獨(dú)特的資源集來(lái)操作涌乳。但是,維護(hù)完全獨(dú)立的資源不是一種好的選擇肉迫,您可能使用鎖苟穆,條件抄课,原子操作和其他技術(shù)來(lái)同步對(duì)資源的訪問(wèn)唱星。
鎖為代碼提供強(qiáng)制形式的保護(hù),一次只能由一個(gè)線程執(zhí)行跟磨。最常見的鎖定類型是互斥鎖魏颓,也稱為互斥鎖。當(dāng)一個(gè)線程試圖獲取當(dāng)前由另一個(gè)線程持有的互斥鎖時(shí)吱晒,它會(huì)阻塞甸饱,直到另一個(gè)線程釋放該鎖。多個(gè)系統(tǒng)框架為互斥鎖提供支持仑濒,盡管它們都基于相同的底層技術(shù)叹话。此外,Cocoa提供了互斥鎖的幾種變體墩瞳,以支持不同類型的行為驼壶,例如遞歸。有關(guān)可用鎖類型的更多信息喉酌,請(qǐng)參閱鎖热凹。
除鎖定外,系統(tǒng)還提供對(duì)條件的支持泪电,以確保應(yīng)用程序中任務(wù)的正確排序般妙。條件充當(dāng)守門人,阻止給定線程相速,直到它表示的條件變?yōu)檎娴臁.?dāng)發(fā)生這種情況時(shí),條件會(huì)釋放線程并允許它繼續(xù)突诬。POSIX層和Foundation框架都為條件提供直接支持苫拍。(如果使用操作對(duì)象,則可以配置操作對(duì)象之間的依賴關(guān)系以對(duì)任務(wù)的執(zhí)行進(jìn)行排序旺隙,這與條件提供的行為非常相似绒极。)
雖然鎖和條件在并發(fā)設(shè)計(jì)中非常常見,但原子操作是保護(hù)和同步數(shù)據(jù)訪問(wèn)的另一種方法蔬捷。在可以對(duì)標(biāo)量數(shù)據(jù)類型執(zhí)行數(shù)學(xué)或邏輯運(yùn)算的情況下垄提,原子操作為鎖定提供了輕量級(jí)替代。原子操作使用特殊的硬件指令來(lái)確保在其他線程有機(jī)會(huì)訪問(wèn)變量之前完成對(duì)變量的修改抠刺。
有關(guān)可用同步工具的詳細(xì)信息塔淤,請(qǐng)參閱同步工具摘昌。
線程間通信
雖然良好的設(shè)計(jì)可以最大限度地減少所需的通信量速妖,但在某些時(shí)候,線程之間的通信變得必要聪黎。(線程的工作是為您的應(yīng)用程序工作罕容,但如果從未使用過(guò)該作業(yè)的結(jié)果备恤,它有什么用處?)線程可能需要處理新的作業(yè)請(qǐng)求或?qū)⑵溥M(jìn)度報(bào)告給應(yīng)用程序的主線程锦秒。在這些情況下露泊,您需要一種方法來(lái)從一個(gè)線程獲取信息到另一個(gè)線程。幸運(yùn)的是旅择,線程共享相同的進(jìn)程空間這一事實(shí)意味著您有很多通信選項(xiàng)惭笑。
線程之間有許多通信方式,每種方式都有自己的優(yōu)點(diǎn)和缺點(diǎn)生真。配置線程局部存儲(chǔ)列出了可以在OS X中使用的最常見的通信機(jī)制沉噩。(除了消息隊(duì)列和Cocoa分布式對(duì)象,這些技術(shù)在iOS中也可用柱蟀。)此表中的技術(shù)按復(fù)雜度遞增川蒙。
表1-3 通信機(jī)制
header 1 | header 2 |
---|---|
直接消息傳遞 | Cocoa應(yīng)用程序支持直接在其他線程上執(zhí)行選擇器的能力。此功能意味著一個(gè)線程基本上可以在任何其他線程上執(zhí)行方法长已。因?yàn)樗鼈兪窃谀繕?biāo)線程的上下文中執(zhí)行的畜眨,所以以這種方式發(fā)送的消息會(huì)在該線程上自動(dòng)序列化。有關(guān)輸入源的信息术瓮,請(qǐng)參閱Cocoa執(zhí)行選擇器源康聂。 |
全局變量,共享內(nèi)存和對(duì)象 | 在兩個(gè)線程之間傳遞信息的另一種簡(jiǎn)單方法是使用全局變量胞四,共享對(duì)象或共享內(nèi)存塊早抠。雖然共享變量快速而簡(jiǎn)單,但它們也比直接消息傳遞更脆弱撬讽。必須使用鎖或其他同步機(jī)制小心保護(hù)共享變量蕊连,以確保代碼的正確性。如果不這樣做可能會(huì)導(dǎo)致競(jìng)爭(zhēng)條件游昼,數(shù)據(jù)損壞或崩潰甘苍。 |
條件 | 條件是一種同步工具,可用于控制線程何時(shí)執(zhí)行特定代碼部分烘豌。您可以將條件視為門衛(wèi)载庭,讓線程僅在滿足所述條件時(shí)運(yùn)行。有關(guān)如何使用條件的信息廊佩,請(qǐng)參閱使用條件囚聚。 |
運(yùn)行循環(huán)源 | 自定義運(yùn)行循環(huán)源是您設(shè)置為在線程上接收特定于應(yīng)用程序的消息的源。因?yàn)樗鼈兪鞘录?qū)動(dòng)的标锄,所以當(dāng)沒(méi)有任何事情要做時(shí)顽铸,運(yùn)行循環(huán)源會(huì)讓你的線程自動(dòng)進(jìn)入休眠狀態(tài),從而提高線程的效率料皇。有關(guān)運(yùn)行循環(huán)和運(yùn)行循環(huán)源的信息谓松,請(qǐng)參閱運(yùn)行循環(huán)星压。 |
端口和套接字 | 基于端口的通信是兩種線程之間通信的更精細(xì)的方式,但它也是一種非彻砥可靠的技術(shù)娜膘。更重要的是,端口和套接字可用于與外部實(shí)體(例如其他進(jìn)程和服務(wù))進(jìn)行通信优质。為了提高效率竣贪,端口是使用運(yùn)行循環(huán)源實(shí)現(xiàn)的,因此當(dāng)端口上沒(méi)有數(shù)據(jù)等待時(shí)巩螃,線程會(huì)休眠贾富。有關(guān)運(yùn)行循環(huán)和基于端口的輸入源的信息,請(qǐng)參閱運(yùn)行循環(huán)牺六。 |
消息隊(duì)列 | 傳統(tǒng)的多處理服務(wù)定義了用于管理傳入和傳出數(shù)據(jù)的先進(jìn)先出(FIFO)隊(duì)列抽象颤枪。盡管消息隊(duì)列簡(jiǎn)單方便,但它們并不像其他一些通信技術(shù)那樣高效淑际。有關(guān)如何使用消息隊(duì)列的詳細(xì)信息畏纲,請(qǐng)參閱“多處理服務(wù)編程指南”。 |
Cocoa分布式對(duì)象 | 分布式對(duì)象是一種Cocoa技術(shù)春缕,可提供基于端口的通信的高級(jí)實(shí)現(xiàn)盗胀。盡管可以將此技術(shù)用于線程間通信,但由于其產(chǎn)生的開銷量很大锄贼,因此非常不鼓勵(lì)這樣做票灰。分布式對(duì)象更適合與其他進(jìn)程通信,其中進(jìn)程之間的開銷已經(jīng)很高宅荤。有關(guān)更多信息屑迂,請(qǐng)參閱分布式對(duì)象編程主題。 |
設(shè)計(jì)技巧
以下部分提供了一些指導(dǎo)原則冯键,可幫助您以確保代碼正確性的方式實(shí)現(xiàn)線程惹盼。其實(shí)一些指南還為自己的線程代碼提供實(shí)現(xiàn)更好性能的技巧。與任何性能技巧一樣惫确,您應(yīng)始終在更改代碼之前手报,期間和之后收集相關(guān)的性能統(tǒng)計(jì)信息。
避免明確的創(chuàng)建線程
編寫線程創(chuàng)建代碼很繁瑣且可能容易出錯(cuò)所以要盡量避免改化。OS X和iOS通過(guò)其他API提供對(duì)并發(fā)的隱式支持掩蛤。比起創(chuàng)建自己的線程,建議應(yīng)用異步APIs陈肛,GCD或者operation objects來(lái)工作揍鸟。這些技術(shù)為您做幕后的線程相關(guān)工作,并保證正確執(zhí)行燥爷。此外蜈亩,通過(guò)根據(jù)當(dāng)前系統(tǒng)負(fù)載調(diào)整活動(dòng)線程數(shù),GCD和operation objects等技術(shù)可以比您自己的代碼更有效地管理線程前翎。有關(guān)GCD和operation objects的更多信息稚配,請(qǐng)參閱“并發(fā)編程指南”。
保持線程合理的忙碌
如果您決定手動(dòng)創(chuàng)建和管理線程港华,請(qǐng)記住線程占用寶貴的系統(tǒng)資源道川。您應(yīng)該盡力確保分配給線程的任何任務(wù)都是合理的活躍和高效的。與此同時(shí)立宜,您不應(yīng)該害怕終止花費(fèi)大部分時(shí)間閑置的線程冒萄。線程使用大量?jī)?nèi)存,其中一些是有線的橙数,因此釋放空閑線程不僅有助于減少應(yīng)用程序的內(nèi)存占用尊流,還可以釋放更多物理內(nèi)存供其他系統(tǒng)進(jìn)程使用。
要點(diǎn): 在開始終止空閑線程之前灯帮,應(yīng)始終記錄應(yīng)用程序當(dāng)前性能的一組基線度量崖技。嘗試更改后,請(qǐng)進(jìn)行其他測(cè)量以驗(yàn)證更改是否實(shí)際上提高了性能钟哥,而不是損壞它迎献。
避免共享數(shù)據(jù)結(jié)構(gòu)
避免與線程相關(guān)的資源沖突的最簡(jiǎn)便和最簡(jiǎn)單的方法是為程序中的每個(gè)線程提供它所需的任何數(shù)據(jù)的副本。當(dāng)您最小化線程之間的通信和資源爭(zhēng)用時(shí)腻贰,并行代碼最有效吁恍。
創(chuàng)建多線程應(yīng)用程序很難。即使您非常小心并在代碼中的所有正確接合點(diǎn)處鎖定共享數(shù)據(jù)結(jié)構(gòu)播演,您的代碼仍可能在語(yǔ)義上不安全冀瓦。例如,如果希望以特定順序修改共享數(shù)據(jù)結(jié)構(gòu)写烤,則代碼可能會(huì)遇到問(wèn)題咕幻。將代碼更改為基于事務(wù)的模型以進(jìn)行補(bǔ)償可能隨后會(huì)抵消具有多個(gè)線程的性能優(yōu)勢(shì)。首先消除資源爭(zhēng)用通常會(huì)導(dǎo)致設(shè)計(jì)更簡(jiǎn)單顶霞,性能更佳肄程。
線程與用戶界面
如果您的應(yīng)用程序具有圖形用戶界面,建議您從應(yīng)用程序的主線程接收與用戶相關(guān)的事件并啟動(dòng)界面更新选浑。此方法有助于避免與處理用戶事件和繪制窗口內(nèi)容相關(guān)的同步問(wèn)題蓝厌。某些框架(如Cocoa)通常需要此行為,但即使對(duì)于那些不這樣做的框架古徒,將此行為保留在主線程上也具有簡(jiǎn)化管理用戶界面的邏輯的優(yōu)勢(shì)拓提。
有一些例外值得注意,從其他線程執(zhí)行圖形操作是有利的隧膘。例如代态,您可以使用輔助線程來(lái)創(chuàng)建和處理圖像寺惫,并執(zhí)行其他與圖像相關(guān)的計(jì)算。使用輔助線程進(jìn)行這些操作可以大大提高性能蹦疑。如果您不確定特定的圖形操作西雀,請(qǐng)計(jì)劃從主線程執(zhí)行此操作
有關(guān)使用Cocoa線程安全的更多信息,請(qǐng)參閱線程安全摘要歉摧。有關(guān)使用Cocoa繪圖的更多信息艇肴,請(qǐng)參閱“ Cocoa繪圖指南”。
退出時(shí)留意線程的行為
進(jìn)程將一直運(yùn)行叁温,直到所有未分離的線程都退出再悼。默認(rèn)情況下,只將應(yīng)用程序的主線程創(chuàng)建為非分離膝但,但您也可以創(chuàng)建其他線程冲九。當(dāng)用戶退出應(yīng)用程序時(shí),通常認(rèn)為立即終止所有分離的線程是合適的行為跟束,因?yàn)榉蛛x線程完成的工作被認(rèn)為是可選的娘侍。但是,如果您的應(yīng)用程序使用后臺(tái)線程將數(shù)據(jù)保存到磁盤或執(zhí)行其他關(guān)鍵工作泳炉,您可能希望將這些線程創(chuàng)建為非分離憾筏,以防止在應(yīng)用程序退出時(shí)丟失數(shù)據(jù)。
將線程創(chuàng)建為非分離(也稱為可連接)需要您做額外的工作花鹅。由于大多數(shù)高級(jí)線程技術(shù)默認(rèn)情況下不創(chuàng)建可連接線程氧腰,因此您可能必須使用POSIX API來(lái)創(chuàng)建線程。此外刨肃,您必須在應(yīng)用程序的主線程中添加代碼古拴,以便在最終退出時(shí)與非分離線程連接。有關(guān)創(chuàng)建可連接線程的信息真友,請(qǐng)參閱設(shè)置線程的分離狀態(tài)黄痪。
如果您正在編寫可可應(yīng)用程序,您還可以使用applicationShouldTerminate:委托方法將應(yīng)用程序的終止延遲到以后的時(shí)間或完全取消它盔然。延遲終止時(shí)桅打,您的應(yīng)用程序需要等待任何關(guān)鍵線程完成其任務(wù),然后調(diào)用該replyToApplicationShouldTerminate:方法愈案。有關(guān)這些方法的詳細(xì)信息挺尾,請(qǐng)參閱NSApplication類參考。
處理特殊情況
異常處理機(jī)制依賴于當(dāng)前調(diào)用堆棧在拋出異常時(shí)執(zhí)行任何必要的清理站绪。因?yàn)槊總€(gè)線程都有自己的調(diào)用堆棧遭铺,所以每個(gè)線程都負(fù)責(zé)捕獲自己的異常。未能在輔助線程中捕獲異常與未在主線程中捕獲異常相同:進(jìn)程會(huì)終止。不能將未捕獲的異常拋出到其他線程進(jìn)行處理魂挂。
如果在當(dāng)前線程捕獲到異常需要通知其他線程(如主線程)甫题。你應(yīng)該捕獲異常并簡(jiǎn)單地向另一個(gè)線程發(fā)送一條消息,指出發(fā)生了什么涂召。根據(jù)您的模型和您要執(zhí)行的操作坠非,捕獲異常的線程可以繼續(xù)處理(如果可能),等待指令芹扭,或者只是退出麻顶。
注意: 在Cocoa中赦抖,NSException對(duì)象是一個(gè)自包含的對(duì)象舱卡,能在被捕獲時(shí)從一個(gè)線程傳遞給另一個(gè)線程。
在某些情況下队萤,可能會(huì)自動(dòng)為您創(chuàng)建異常處理程序轮锥。例如,@synchronizedObjective-C中的指令包含一個(gè)隱式異常處理程序要尔。
干凈的終止線程
線程退出的最佳方法當(dāng)然是讓它到達(dá)主入口點(diǎn)例程的末尾舍杜。雖然有立即終止線程的函數(shù),但這些函數(shù)應(yīng)該僅作為最后的手段使用赵辕。在線程到達(dá)其自然終點(diǎn)之前終止線程阻礙了線程自行清理既绩。如果線程已分配內(nèi)存,打開文件或獲取其他類型的資源还惠,則代碼可能無(wú)法回收這些資源饲握,從而導(dǎo)致內(nèi)存泄漏或其他潛在問(wèn)題。
有關(guān)退出線程的正確方法的更多信息蚕键,參閱請(qǐng)終止線程救欧。
代碼庫(kù)的線程安全
雖然應(yīng)用程序開發(fā)人員可以控制應(yīng)用程序是否使用多個(gè)線程執(zhí)行,但庫(kù)開發(fā)人員卻沒(méi)有锣光。在開發(fā)庫(kù)時(shí)笆怠,您必須假設(shè)調(diào)用應(yīng)用程序是多線程的,或者可以隨時(shí)切換為多線程誊爹。因此蹬刷,您應(yīng)始終對(duì)代碼的關(guān)鍵部分使用鎖。
對(duì)于庫(kù)開發(fā)人員频丘,僅在應(yīng)用程序變?yōu)槎嗑€程時(shí)才創(chuàng)建鎖是不明智的箍铭。如果您需要在某個(gè)時(shí)刻鎖定代碼,請(qǐng)?jiān)谑褂脦?kù)的早期創(chuàng)建鎖定對(duì)象椎镣,最好是在某種初始化庫(kù)時(shí)顯式調(diào)用诈火。雖然您也可以使用靜態(tài)庫(kù)初始化函數(shù)來(lái)創(chuàng)建此類鎖,但只有在沒(méi)有其他方法時(shí)才嘗試這樣做。執(zhí)行初始化函數(shù)會(huì)增加加載庫(kù)所需的時(shí)間冷守,并可能對(duì)性能產(chǎn)生負(fù)面影響刀崖。
注意:始終記得在庫(kù)代碼中平衡lock和unlock互斥鎖的調(diào)用。還應(yīng)該記住鎖定庫(kù)中數(shù)據(jù)結(jié)構(gòu)拍摇,而不是依賴調(diào)用代碼來(lái)提供線程安全的環(huán)境亮钦。
如果您正在開發(fā)Cocoa庫(kù),則可以為NSWillBecomeMultiThreadedNotification注冊(cè)觀察者充活,以便在應(yīng)用程序變?yōu)槎嗑€程時(shí)通知您蜂莉。但是,您不應(yīng)該依賴于接收此通知混卵,因?yàn)樗赡軙?huì)在調(diào)用庫(kù)代碼之前調(diào)度線程映穗。