線程管理Mac OS X和iOS里面的每個進(jìn)程都是有一個或多個線程構(gòu)成,每個線程都代表一個代碼的執(zhí)行路徑。每個應(yīng)用程序啟動時候都是一個線程,它執(zhí)行程序的main函數(shù)。應(yīng)用程序可以生成額外的線程,其中每個線程執(zhí)行一個特定功能的代碼。 當(dāng)應(yīng)用程序生成一個新的線程的時候,該線程變成應(yīng)用程序進(jìn)程空間內(nèi)的一個實(shí)體。每個線程都擁有它自己的執(zhí)行堆棧,由內(nèi)核調(diào)度獨(dú)立的運(yùn)行時間片。一個線程可以和其他線程或其他進(jìn)程通信,執(zhí)行I/O操作,甚至執(zhí)行任何你想要它完成的任務(wù)。因為它們處于相同的進(jìn)程空間,所以一個獨(dú)立應(yīng)用程序里面的所有線程共享相同的虛擬內(nèi)存空間,并且具有和進(jìn)程相同的訪問權(quán)限。 本章提供了Mac OS X和iOS上面可用線程技術(shù)的預(yù)覽,并給出了如何在你的應(yīng)用程序里面使用它們的例子。
1.1 線程成本多線程會占用你應(yīng)用程序(和系統(tǒng)的)的內(nèi)存使用和性能方面的資源。每個線程都需要分配一定的內(nèi)核內(nèi)存和應(yīng)用程序內(nèi)存空間的內(nèi)存。管理你的線程和協(xié)調(diào)其調(diào)度所需的核心數(shù)據(jù)結(jié)構(gòu)存儲在使用Wired Memory的內(nèi)核里面。你線程的堆棧空間和每個線程的數(shù)據(jù)都被存儲在你應(yīng)用程序的內(nèi)存空間里面。這些數(shù)據(jù)結(jié)構(gòu)里面的大部分都是當(dāng)你首次創(chuàng)建線程或者進(jìn)程的時候被創(chuàng)建和初始化的,它們所需的代價成本很高,因為需要和內(nèi)核交互。 表2-1量化了在你應(yīng)用程序創(chuàng)建一個新的用戶級線程所需的大致成本。這些成本里面的部分是可配置的,比如為輔助線程分配堆棧空間的大小。創(chuàng)建一個線程所需的時間成本是粗略估計的,僅用于當(dāng)互相比較的時候。線程創(chuàng)建時間很大程度依賴于處理器的負(fù)載,計算速度,和可用的系統(tǒng)和程序空間。 Table 2-1 Thread creation costs
注意:因為底層內(nèi)核的支持,操作對象(Operation objectis)可能創(chuàng)建線程更快。它們使用內(nèi)核里面常駐線程池里面的線程來節(jié)省創(chuàng)建的時間,而不是每次都創(chuàng)建新的線程。關(guān)于更多使用操作對象(Operation objects)的信息,參閱并發(fā)編程指南(Concurrency Programming Guide)。 當(dāng)編寫線程代碼時另外一個需要考慮的成本是生產(chǎn)成本。設(shè)計一個線程應(yīng)用程序有時會需要根本性改變你應(yīng)用程序數(shù)據(jù)結(jié)構(gòu)的組織方式。要做這些改變可能需要避免使用同步,因為本身設(shè)計不好的應(yīng)用可能會造成巨大的性能損失。設(shè)計這些數(shù)據(jù)結(jié)構(gòu)和在線程代碼里面調(diào)試問題會增加開發(fā)一個線程應(yīng)用所需的時間。然而避免這些消耗的話,可能在運(yùn)行時候帶來更大的問題,如果你的多線程花費(fèi)太多的時間在鎖的等待而沒有做任何事情。 1.1 創(chuàng)建一個線程創(chuàng)建低級別的線程相對簡單。在所有情況下,你必須有一個函數(shù)或方法作為線程的主入口點(diǎn),你必須使用一個可用的線程例程啟動你的線程。以下幾個部分介紹了比較常用線程創(chuàng)建的基本線程技術(shù)。線程創(chuàng)建使用了這些技術(shù)的繼承屬性的默認(rèn)設(shè)置,由你所使用的技術(shù)來決定。關(guān)于更多如何配置你的線程的信息,參閱“線程屬性配置”部分。 1.1.1 使用NSThread使用NSThread來創(chuàng)建線程有兩個可以的方法:
這兩種創(chuàng)建線程的技術(shù)都在你的應(yīng)用程序里面新建了一個脫離的線程。一個脫離的線程意味著當(dāng)線程退出的時候線程的資源由系統(tǒng)自動回收。這也同樣意味著之后不需要在其他線程里面顯式的連接(join)。因為detachNewThreadSelctor:toTarget:withObject:方法在Mac OS X的任何版本都支持,所以在Cocoa應(yīng)用里面使用多線程的地方經(jīng)??梢园l(fā)現(xiàn)它。為了生成一個新的線程,你只要簡單的提供你想要使用為線程主體入口的方法的名稱(被指定為一個selector),和任何你想在啟動時傳遞給線程的數(shù)據(jù)。下面的示例演示了這種方法的基本調(diào)用,來使用當(dāng)前對象的自定義方法來生成一個線程。
在Mac OS X v10.5及其之后初始化一個NSThread對象的簡單方法是使用initWithTarget:selector:object:方法。該方法和detachNewThreadSelector:toTarget:withObject:方法來初始化一個新的NSThread實(shí)例需要相同的額外開銷。然而它并沒有啟動一個線程。為了啟動一個線程,你可以顯式調(diào)用先對象的start方法,如下面代碼: 注意:使用initWithTarget:selector:object:方法的替代辦法是子類化NSThread,并重寫它的main方法。你可以使用你重寫的該方法的版本來實(shí)現(xiàn)你線程的主體入口。更多信息,請參閱NSThread Class Reference里面子類化的提示。 如果你擁有一個NSThread對象,它的線程當(dāng)前真正運(yùn)行,你可以給該線程發(fā)送消息的唯一方法是在你應(yīng)用程序里面的任何對象使用performSelector:onThread:withObject:waitUntilDone:方法。在Mac OS X v10.5支持在多線程上面執(zhí)行selectors(而不是在主線程里面),并且它是實(shí)現(xiàn)線程間通信的便捷方法。你使用該技術(shù)時所發(fā)送的消息會被其他線程作為run-loop主體的一部分直接執(zhí)行(當(dāng)然這些意味著目標(biāo)線程必須在它的run loop里面運(yùn)行,參閱“ Run Loops”)。當(dāng)你使用該方法來實(shí)現(xiàn)線程通信的時候,你可能仍然需要一個同步操作,但是這比在線程間設(shè)置通信端口簡單多了。 注意:雖然在線程間的偶爾通信的時候使用該方法很好,但是你不能周期的或頻繁的使用performSelector:onThread:withObject:waitUntilDone:來實(shí)現(xiàn)線程間的通信。 關(guān)于線程間通信的可選方法,參閱“設(shè)置線程的脫離狀態(tài)”部分。 1.1.2 使用POSIX的多線程Mac OS X和iOS提供基于C語言支持的使用POSIX線程API來創(chuàng)建線程的方法。該技術(shù)實(shí)際上可以被任何類型的應(yīng)用程序使用(包括Cocoa和Cocoa Touch的應(yīng)用程序),并且如果你當(dāng)前真為多平臺開發(fā)應(yīng)用的話,該技術(shù)可能更加方便。你使用來創(chuàng)建線程的POSIX例程被調(diào)用的時候,使用pthread_create剛好足夠。 列表2-1顯示了兩個使用POSIX來創(chuàng)建線程的自定義函數(shù)。LaunchThread函數(shù)創(chuàng)建了一個新的線程,該線程的例程由PosixThreadMainRoutine函數(shù)來實(shí)現(xiàn)。因為POSIX創(chuàng)建的線程默認(rèn)情況是可連接的(joinable),下面的例子改變線程的屬性來創(chuàng)建一個脫離的線程。把線程標(biāo)記為脫離的,當(dāng)它退出的時候讓系統(tǒng)有機(jī)會立即回收該線程的資源。 Listing 2-1 Creating a thread in C
如果你把上面列表的代碼添加到你任何一個源文件,并且調(diào)用LaunchThread函數(shù),它將會在你的應(yīng)用程序里面創(chuàng)建一個新的脫離線程。當(dāng)然,新創(chuàng)建的線程使用該代碼沒有做任何有用的事情。線程將會加載并立即退出。為了讓它更有興趣,你需要添加代碼到PosixThreadMainRoutine函數(shù)里面來做一些實(shí)際的工作。為了保證線程知道該干什么,你可以在創(chuàng)建的時候給線程傳遞一個數(shù)據(jù)的指針。把該指針作為pthread_create的最后一個參數(shù)。 為了在新建的線程里面和你應(yīng)用程序的主線程通信,你需要建立一條和目標(biāo)線程之間的穩(wěn)定的通信路徑。對于基于C語言的應(yīng)用程序,有幾種辦法來實(shí)現(xiàn)線程間的通信,包括使用端口(ports),條件(conditions)和共享內(nèi)存(shared memory)。對于長期存在的線程,你應(yīng)該幾乎總是成立某種線程間的通信機(jī)制,讓你的應(yīng)用程序的主線程有辦法來檢查線程的狀態(tài)或在應(yīng)用程序退出時干凈關(guān)閉它。 關(guān)于更多介紹POSIX線程函數(shù)的信息,參閱pthread的主頁。 1.1.3 使用NSObject來生成一個線程在iOS和Mac OS X v10.5及其之后,所有的對象都可能生成一個新的線程,并用它來執(zhí)行它任意的方法。方法performSelectorInBackground:withObject:新生成一個脫離的線程,使用指定的方法作為新線程的主體入口點(diǎn)。比如,如果你有一些對象(使用變量myObj來代表),并且這些對象擁有一個你想在后臺運(yùn)行的doSomething的方法,你可以使用如下的代碼來生成一個新的線程: 調(diào)用該方法的效果和你在當(dāng)前對象里面使用NSThread的detachNewThreadSelector:toTarget:withObject:傳遞selectore,object作為參數(shù)的方法一樣。新的線程將會被立即生成并運(yùn)行,它使用默認(rèn)的設(shè)置。在selectore內(nèi)部,你必須配置線程就像你在任何線程里面一樣。比如,你可能需要設(shè)置一個自動釋放池(如果你沒有使用垃圾回收機(jī)制),在你要使用它的時候配置線程的run loop。關(guān)于更是介紹如果配置線程的信息,參閱“配置線程屬性”部分。 1.1.4 使用其他線程技術(shù)盡管POSIX例程和NSThread類被推薦使用來創(chuàng)建低級線程,但是其他基于C語言的技術(shù)在Mac OS X上面同樣可用。在這其中,唯一一個可以考慮使用的是多處理服務(wù)(Multiprocessing Services),它本身就是在POSIX線程上執(zhí)行。多處理服務(wù)是專門為早期的Mac OS版本開發(fā)的,后來在Mac OS X里面的Carbon應(yīng)用程序上面同樣適用。如果你有代碼真是有該技術(shù),你可以繼續(xù)使用它,盡管你應(yīng)該把這些代碼轉(zhuǎn)化為POSIX。該技術(shù)在iOS上面不可用。 關(guān)于更多如何使用多處理服務(wù)的信息,參閱多處理服務(wù)編程指南(Multiprocessing Services Programming Guide)。 1.1.5 在Cocoa程序上面使用POSIX線程經(jīng)管NSThread類是Cocoa應(yīng)用程序里面創(chuàng)建多線程的主要接口,如果可以更方便的話你可以任意使用POSIX線程帶替代。例如,如果你的代碼里面已經(jīng)使用了它,而你又不想改寫它的話,這時你可能需要使用POSIX多線程。如果你真打算在Cocoa程序里面使用POSIX線程,你應(yīng)該了解如果在Cocoa和線程間交互,并遵循以下部分的一些指南。 u Cocoa框架的保護(hù) 對于多線程的應(yīng)用程序,Cocoa框架使用鎖和其他同步方式來保證代碼的正確執(zhí)行。為了保護(hù)這些鎖造成在單線程里面性能的損失,Cocoa直到應(yīng)用程序使用NSThread類生成它的第一個新的線程的時候才創(chuàng)建這些鎖。如果你僅且使用POSIX例程來生成新的線程,Cocoa不會收到關(guān)于你的應(yīng)用程序當(dāng)前變?yōu)槎嗑€程的通知。當(dāng)這些剛好發(fā)生的時候,涉及Cocoa框架的操作哦可能會破壞甚至讓你的應(yīng)用程序崩潰。 為了讓Cocoa知道你正打算使用多線程,你所需要做的是使用NSThread類生成一個線程,并讓它立即退出。你線程的主體入口點(diǎn)不需要做任何事情。只需要使用NSThread來生成一個線程就足夠保證Cocoa框架所需的鎖到位。 如果你不確定Cocoa是否已經(jīng)知道你的程序是多線程的,你可以使用NSThread的isMultiThreaded方法來檢驗一下。 u 混合POSIX和Cocoa的鎖 在同一個應(yīng)用程序里面混合使用POSIX和Cocoa的鎖很安全。Cocoa鎖和條件對象基本上只是封裝了POSIX的互斥體和條件。然而給定一個鎖,你必須總是使用同樣的接口來創(chuàng)建和操縱該鎖。換言之,你不能使用Cocoa的NSLock對象來操縱一個你使用pthread_mutex_init函數(shù)生成的互斥體,反之亦然。 1.2 配置線程屬性創(chuàng)建線程之后,或者有時候是之前,你可能需要配置不同的線程環(huán)境。以下部分描述了一些你可以做的改變,和在什么時候你需要做這些改變。 1.2.1 配置線程的堆棧大小對于每個你新創(chuàng)建的線程,系統(tǒng)會在你的進(jìn)程空間里面分配一定的內(nèi)存作為該線程的堆棧。該堆棧管理堆棧幀,也是任何線程局部變量聲明的地方。給線程分配的內(nèi)存大小在“線程成本”里面已經(jīng)列舉了。 如果你想要改變一個給定線程的堆棧大小,你必須在創(chuàng)建該線程之前做一些操作。所有的線程技術(shù)提供了一些辦法來設(shè)置線程堆棧的大小。雖然可以使用NSThread來設(shè)置堆棧大小,但是它只能在iOS和Mac OS X v10.5及其之后才可用。表2-2列出了每種技術(shù)的對于不同的操作。 Table 2-2 Setting the stack size of a thread
1.2.2 配置線程本地存儲每個線程都維護(hù)了一個鍵-值的字典,它可以在線程里面的任何地方被訪問。你可以使用該字典來保存一些信息,這些信息在整個線程的執(zhí)行過程中都保持不變。比如,你可以使用它來存儲在你的整個線程過程中Run loop里面多次迭代的狀態(tài)信息。 Cocoa和POSIX以不同的方式保存線程的字典,所以你不能混淆并同時調(diào)用者兩種技術(shù)。然而只要你在你的線程代碼里面堅持使用了其中一種技術(shù),最終的結(jié)果應(yīng)該是一樣的。在Cocoa里面,你使用NSThread的threadDictionary方法來檢索一個NSMutableDictionary對象,你可以在它里面添加任何線程需要的鍵。在POSIX里面,你使用pthread_setspecific和pthread_getspecific函數(shù)來設(shè)置和訪問你線程的鍵和值。 1.2.3 設(shè)置線程的脫離狀態(tài)大部分上層的線程技術(shù)都默認(rèn)創(chuàng)建了脫離線程(Datached thread)。大部分情況下,脫離線程(Detached thread)更受歡迎,因為它們允許系統(tǒng)在線程完成的時候立即釋放它的數(shù)據(jù)結(jié)構(gòu)。脫離線程同時不需要顯示的和你的應(yīng)用程序交互。意味著線程檢索的結(jié)果由你來決定。相比之下,系統(tǒng)不回收可連接線程(Joinable thread)的資源直到另一個線程明確加入該線程,這個過程可能會阻止線程執(zhí)行加入。 你可以認(rèn)為可連接線程類似于子線程。雖然你作為獨(dú)立線程運(yùn)行,但是可連接線程在它資源可以被系統(tǒng)回收之前必須被其他線程連接。可連接線程同時提供了一個顯示的方式來把數(shù)據(jù)從一個正在退出的線程傳遞到其他線程。在它退出之前,可連接線程可以傳遞一個數(shù)據(jù)指針或者其他返回值給pthread_exit函數(shù)。其他線程可以通過pthread_join函數(shù)來拿到這些數(shù)據(jù)。 重要:在應(yīng)用程序退出時,脫離線程可以立即被中斷,而可連接線程則不可以。每個可連接線程必須在進(jìn)程被允許可以退出的時候被連接。所以當(dāng)線程處于周期性工作而不允許被中斷的時候,比如保存數(shù)據(jù)到硬盤,可連接線程是最佳選擇。 如果你想要創(chuàng)建可連接線程,唯一的辦法是使用POSIX線程。POSIX默認(rèn)創(chuàng)建的線程是可連接的。為了把線程標(biāo)記為脫離的或可連接的,使用pthread_attr_setdetachstate函數(shù)來修改正在創(chuàng)建的線程的屬性。在線程啟動后,你可以通過調(diào)用pthread_detach函數(shù)來把線程修改為可連接的。關(guān)于更多POSIX線程函數(shù)信息,參與pthread主頁。關(guān)于更多如果連接一個線程,參閱pthread_join的主頁。 1.2.4 設(shè)置線程的優(yōu)先級你創(chuàng)建的任何線程默認(rèn)的優(yōu)先級是和你本身線程相同。內(nèi)核調(diào)度算法在決定該運(yùn)行那個線程時,把線程的優(yōu)先級作為考量因素,較高優(yōu)先級的線程會比較低優(yōu)先級的線程具有更多的運(yùn)行機(jī)會。較高優(yōu)先級不保證你的線程具體執(zhí)行的時間,只是相比較低優(yōu)先級的線程,它更有可能被調(diào)度器選擇執(zhí)行而已。 重要:讓你的線程處于默認(rèn)優(yōu)先級值是一個不錯的選擇。增加某些線程的優(yōu)先級,同時有可能增加了某些較低優(yōu)先級線程的饑餓程度。如果你的應(yīng)用程序包含較高優(yōu)先級和較低優(yōu)先級線程,而且它們之間必須交互,那么較低優(yōu)先級的饑餓狀態(tài)有可能阻塞其他線程,并造成性能瓶頸。 如果你想改變線程的優(yōu)先級,Cocoa和POSIX都提供了一種方法來實(shí)現(xiàn)。對于Cocoa線程而言,你可以使用NSThread的setThreadPriority:類方法來設(shè)置當(dāng)前運(yùn)行線程的優(yōu)先級。對于POSIX線程,你可以使用pthread_setschedparam函數(shù)來實(shí)現(xiàn)。關(guān)于更多信息,參與NSThread Class Reference或pthread_setschedparam主頁。 1.3 編寫你線程的主體入口點(diǎn)對于大部分而言,Mac OS X上面線程結(jié)構(gòu)的主體入口點(diǎn)和其他平臺基本一樣。你需要初始化你的數(shù)據(jù)結(jié)構(gòu),做一些工作或可行的設(shè)置一個run loop,并在線程代碼被執(zhí)行完后清理它。根據(jù)設(shè)計,當(dāng)你寫的主體入口點(diǎn)的時候有可能需要采取一些額外的步驟。 1.3.1 創(chuàng)建一個自動釋放池(Autorelease Pool)在Objective - C框架鏈接的應(yīng)用程序,通常在它們的每一個線程必須創(chuàng)建至少一個自動釋放池。如果應(yīng)用程序使用管理模型,即應(yīng)用程序處理的retain和release對象,那么自動釋放池捕獲任何從該線程autorelease的對象。 如果應(yīng)用程序使用的垃圾回收機(jī)制,而不是管理的內(nèi)存模型,那么創(chuàng)建一個自動釋放池不是絕對必要的。在垃圾回收的應(yīng)用程序里面,一個自動釋放池是無害的,而且大部分情況是被忽略。允許通過個代碼管理必須同時支持垃圾回收和內(nèi)存管理模型。在這種情況下,內(nèi)存管理模型必須支持自動釋放池,當(dāng)應(yīng)用程序運(yùn)行垃圾回收的時候,自動釋放池只是被忽略而已。 如果你的應(yīng)用程序使用內(nèi)存管理模型,在你編寫線程主體入口的時候第一件事情就是創(chuàng)建一個自動釋放池。同樣,在你的線程最后應(yīng)該銷毀該自動釋放池。該池保證自動釋放。雖然對象被調(diào)用,但是它們不被release直到線程退出。列表2-2顯示了線程主體入口使用自動釋放池的基本結(jié)構(gòu)。 Listing 2-2 Defining your thread entry point routine 因為高級的自動釋放池不會釋放它的對象直到線程退出。長時運(yùn)行的線程需求新建額外的自動釋放池來更頻繁的釋放它的對象。比如,一個使用run loop的線程可能在每次運(yùn)行完一次循環(huán)的時候創(chuàng)建并釋放該自動釋放池。更頻繁的釋放對象可以防止你的應(yīng)用程序內(nèi)存占用太大造成性能問題。雖然對于任何與性能相關(guān)的行為,你應(yīng)該測量你代碼的實(shí)際表現(xiàn),并適當(dāng)?shù)卣{(diào)整使用自動釋放池。 關(guān)于更多內(nèi)存管理的信息和自動釋放池,參閱“內(nèi)存高級管理編程指南(Advanced Memory Management Programming Guide)”。 1.3.2 設(shè)置異常處理如果你的應(yīng)用程序捕獲并處理異常,那么你的線程代碼應(yīng)該時刻準(zhǔn)備捕獲任何可能發(fā)生的異常。雖然最好的辦法是在異常發(fā)生的地方捕獲并處理它,但是如果在你的線程里面捕獲一個拋出的異常失敗的話有可能造成你的應(yīng)用程序強(qiáng)退。在你線程的主體入口點(diǎn)安裝一個try/catch模塊,可以讓你捕獲任何未知的異常,并提供一個合適的響應(yīng)。 當(dāng)在Xcode構(gòu)建你項目的時候,你可以使用C 或者Objective-C的異常處理風(fēng)格。 關(guān)于更多設(shè)置如何在Objective-C里面拋出和捕獲異常的信息,參閱Exception Programming Topics。 1.3.3 設(shè)置一個Run Loop當(dāng)你想編寫一個獨(dú)立運(yùn)行的線程時,你有兩種選擇。第一種選擇是寫代碼作為一個長期的任務(wù),很少甚至不中斷,線程完成的時候退出。第二種選擇是把你的線程放入一個循環(huán)里面,讓它動態(tài)的處理到來的任務(wù)請求。第一種方法不需要在你的代碼指定任何東西;你只需要啟動的時候做你打算做的事情即可。然而第二種選擇需要在你的線程里面添加一個run loop。 Mac OS X和iOS提供了在每個線程實(shí)現(xiàn)run loop內(nèi)置支持。Cocoa、Carbon和UIKit自動在你應(yīng)用程序的主線程啟動一個run loop,但是如果你創(chuàng)建任何輔助線程,你必須手工的設(shè)置一個run loop并啟動它。 關(guān)于更多使用和配置run loop的信息,參閱“Run Loops”部分。 1.4 中斷線程退出一個線程推薦的方法是讓它在它主體入口點(diǎn)正常退出。經(jīng)管Cocoa、POSIX和Multiprocessing Services提供了直接殺死線程的例程,但是使用這些例程是強(qiáng)烈不鼓勵的。殺死一個線程阻止了線程本身的清理工作。線程分配的內(nèi)存可能造成泄露,并且其他線程當(dāng)前使用的資源可能沒有被正確清理干凈,之后造成潛在的問題。 如果你的應(yīng)用程序需要在一個操作中間中斷一個線程,你應(yīng)該設(shè)計你的線程響應(yīng)取消或退出的消息。對于長時運(yùn)行的操作,這意味著周期性停止工作來檢查該消息是否到來。如果該消息的確到來并要求線程退出,那么線程就有機(jī)會來執(zhí)行任何清理和退出工作;否則,它返回繼續(xù)工作和處理下一個數(shù)據(jù)塊。 響應(yīng)取消消息的一個方法是使用run loop的輸入源來接收這些消息。列表2-3顯示了該結(jié)構(gòu)的類似代碼在你的線程的主體入口里面是怎么樣的(該示例顯示了主循環(huán)部分,不包括設(shè)立一個自動釋放池或配置實(shí)際的工作步驟)。該示例在run loop上面安裝了一個自定義的輸入源,它可以從其他線程接收消息。關(guān)于更多設(shè)置輸入源的信息,參閱“配置Run Loop源”。執(zhí)行工作的總和的一部分后,線程運(yùn)行的run loop來查看是否有消息抵達(dá)輸入源。如果沒有,run loop立即退出,并且循環(huán)繼續(xù)處理下一個數(shù)據(jù)塊。因為該處理器并沒有直接的訪問exitNow局部變量,退出條件是通過線程的字典來傳輸?shù)摹?/p> Listing 2-3 Checking for an exit condition during a long job
|
|