自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

iOS多線程編程指南(四)線程同步

移動(dòng)開發(fā) iOS
涉及到線程安全時(shí),一個(gè)好的設(shè)計(jì)是最好的保護(hù)。避免共享資源,并盡量減少線程間的相互作用,這樣可以讓它們減少互相的干擾。但是一個(gè)完全無(wú)干擾的設(shè)計(jì)是不可能的。在線程必須交互的情況下,你需要使用同步工具,來(lái)確保當(dāng)它們交互的時(shí)候是安全的。

應(yīng)用程序里面多個(gè)線程的存在引發(fā)了多個(gè)執(zhí)行線程安全訪問(wèn)資源的潛在問(wèn)題。兩個(gè)線程同時(shí)修改同一資源有可能以意想不到的方式互相干擾。比如,一個(gè)線程可能覆蓋其他線程改動(dòng)的地方,或讓應(yīng)用程序進(jìn)入一個(gè)未知的潛在無(wú)效狀態(tài)。如果你幸運(yùn)的話,受損的資源可能會(huì)導(dǎo)致明顯的性能問(wèn)題或崩潰,這樣比較容易跟蹤并修復(fù)它。然而如果你不走運(yùn),資源受損可能導(dǎo)致微妙的錯(cuò)誤,這些錯(cuò)誤不會(huì)立即顯現(xiàn)出來(lái),而是很久之后才出現(xiàn),或者導(dǎo)致其他可能需要一個(gè)底層的編碼來(lái)顯著修復(fù)的錯(cuò)誤。

但涉及到線程安全時(shí),一個(gè)好的設(shè)計(jì)是最好的保護(hù)。避免共享資源,并盡量減少線程間的相互作用,這樣可以讓它們減少互相的干擾。但是一個(gè)完全無(wú)干擾的設(shè)計(jì)是不可能的。在線程必須交互的情況下,你需要使用同步工具,來(lái)確保當(dāng)它們交互的時(shí)候是安全的。

Mac OS X和iOS提供了你可以使用的多個(gè)同步工具,從提供互斥訪問(wèn)你程序的有序的事件的工具等。以下個(gè)部分介紹了這些工具和如何在代碼中使用他們來(lái)影響安全的訪問(wèn)程序的資源。

1.同步工具

為了防止不同線程意外修改數(shù)據(jù),你可以設(shè)計(jì)你的程序沒有同步問(wèn)題,或你也可以使用同步工具。盡管完全避免出現(xiàn)同步問(wèn)題相對(duì)更好一點(diǎn),但是幾乎總是無(wú)法實(shí)現(xiàn)。以下個(gè)部分介紹了你可以使用的同步工具的基本類別。

1.1 原子操作

原子操作是同步的一個(gè)簡(jiǎn)單的形式,它處理簡(jiǎn)單的數(shù)據(jù)類型。原子操作的優(yōu)勢(shì)是它們不妨礙競(jìng)爭(zhēng)的線程。對(duì)于簡(jiǎn)單的操作,比如遞增一個(gè)計(jì)數(shù)器,原子操作比使用鎖具有更高的性能優(yōu)勢(shì)。

Mac OS X和iOS包含了許多在32位和64位執(zhí)行基本的數(shù)學(xué)和邏輯運(yùn)算的操作。這些操作都使用了原子版本來(lái)操作比較和交換,測(cè)試和設(shè)置,測(cè)試和清理等。查看支持原子操作的列表,參閱/user/include/libkern/OSAtomic.h頭文件和參見atomic主頁(yè)。

1.2 內(nèi)存屏障和 Volatile 變量

為了達(dá)到最佳性能,編譯器通常會(huì)對(duì)匯編基本的指令進(jìn)行重新排序來(lái)盡可能保持處理器的指令流水線。作為優(yōu)化的一部分,編譯器有可能對(duì)訪問(wèn)主內(nèi)存的指令,如果它認(rèn)為這有可能產(chǎn)生不正確的數(shù)據(jù)時(shí),將會(huì)對(duì)指令進(jìn)行重新排序。不幸的是,靠編譯器檢測(cè)到所有可能內(nèi)存依賴的操作幾乎總是不太可能的。如果看似獨(dú)立的變量實(shí)際上是相互影響,那么編譯器優(yōu)化有可能把這些變量更新位錯(cuò)誤的順序,導(dǎo)致潛在不不正確結(jié)果。

內(nèi)存屏障(memory barrier)是一個(gè)使用來(lái)確保內(nèi)存操作按照正確的順序工作的非阻塞的同步工具。內(nèi)存屏障的作用就像一個(gè)柵欄,迫使處理器來(lái)完成位于障礙前面的任何加載和存儲(chǔ)操作,才允許它執(zhí)行位于屏障之后的加載和存儲(chǔ)操作。內(nèi)存屏障同樣使用來(lái)確保一個(gè)線程(但對(duì)另外一個(gè)線程可見)的內(nèi)存操作總是按照預(yù)定的順序完成。如果在這些地方缺少內(nèi)存屏障有可能讓其他線程看到看似不可能的結(jié)果(比如,內(nèi)存屏障的維基百科條目)。為了使用一個(gè)內(nèi)存屏障,你只要在你代碼里面需要的地方簡(jiǎn)單的調(diào)用OSMemoryBarrier函數(shù)。

Volatile 變量適用于獨(dú)立變量的另一個(gè)內(nèi)存限制類型。編譯器優(yōu)化代碼通過(guò)加載這些變量的值進(jìn)入寄存器。對(duì)于本地變量,這通常不會(huì)有什么問(wèn)題。但是如果一個(gè)變量對(duì)另外一個(gè)線程可見,那么這種優(yōu)化可能會(huì)阻止其他線程發(fā)現(xiàn)變量的任何變化。在變量之前加上關(guān)鍵字volatile可以強(qiáng)制編譯器每次使用變量的時(shí)候都從內(nèi)存里面加載。如果一個(gè)變量的值隨時(shí)可能給編譯器無(wú)法檢測(cè)的外部源更改,那么你可以把該變量聲明為volatile變量。

因?yàn)閮?nèi)存屏障和volatile變量降低了編譯器可執(zhí)行的優(yōu)化,因此你應(yīng)該謹(jǐn)慎使用它們,只在有需要的地方時(shí)候,以確保正確性。關(guān)于更多使用內(nèi)存屏障的信息,參閱OSMemoryBarrier主頁(yè)。

1.3 鎖

鎖是最常用的同步工具。你可以是使用鎖來(lái)保護(hù)臨界區(qū)(critical section),這些代碼段在同一個(gè)時(shí)間只能允許被一個(gè)線程訪問(wèn)。比如,一個(gè)臨界區(qū)可能會(huì)操作一個(gè)特定的數(shù)據(jù)結(jié)構(gòu),或使用了每次只能一個(gè)客戶端訪問(wèn)的資源。

表4-1列出了程序最常使用的鎖。Mac OS X和iOS提供了這些鎖里面大部分類型的實(shí)現(xiàn),但是并不是全部實(shí)現(xiàn)。對(duì)于不支持的鎖類型,說(shuō)明列解析了為什么這些鎖不能直接在平臺(tái)上面實(shí)現(xiàn)的原因。

Table 4-1  Lock types

Lock

Description

Mutex

[互斥鎖]

A mutually exclusive (or mutex) lock acts as a protective barrier around a resource. A mutex is a type of semaphore that grants access to only one thread at a time. If a mutex is in use and another thread tries to acquire it, that thread blocks until the mutex is released by its original holder. If multiple threads compete for the same mutex, only one at a time is allowed access to it.

Recursive lock

[遞歸鎖]

A recursive lock is a variant on the mutex lock. A recursive lock allows a single thread to acquire the lock multiple times before releasing it. Other threads remain blocked until the owner of the lock releases the lock the same number of times it acquired it. Recursive locks are used during recursive iterations primarily but may also be used in cases where multiple methods each need to acquire the lock separately.

Read-write lock
[讀寫鎖]

A read-write lock is also referred to as a shared-exclusive lock. This type of lock is typically used in larger-scale operations and can significantly improve performance if the protected data structure is read frequently and modified only occasionally. During normal operation, multiple readers can access the data structure simultaneously. When a thread wants to write to the structure, though, it blocks until all readers release the lock, at which point it acquires the lock and can update the structure. While a writing thread is waiting for the lock, new reader threads block until the writing thread is finished. The system supports read-write locks using POSIX threads only. For more information on how to use these locks, see the pthread man page.

Distributed lock
[分布鎖]

A distributed lock provides mutually exclusive access at the process level. Unlike a true mutex, a distributed lock does not block a process or prevent it from running. It simply reports when the lock is busy and lets the process decide how to proceed.

Spin lock
[自旋鎖]

A spin lock polls its lock condition repeatedly until that condition becomes true. Spin locks are most often used on multiprocessor systems where the expected wait time for a lock is small. In these situations, it is often more efficient to poll than to block the thread, which involves a context switch and the updating of thread data structures. The system does not provide any implementations of spin locks because of their polling nature, but you can easily implement them in specific situations. For information on implementing spin locks in the kernel, see Kernel Programming Guide.

Double-checked lock
[雙重檢查鎖]

A double-checked lock is an attempt to reduce the overhead of taking a lock by testing the locking criteria prior to taking the lock. Because double-checked locks are potentially unsafe, the system does not provide explicit support for them and their use is discouraged.[注意系統(tǒng)不顯式支持該鎖類型]

注意:大部分鎖類型都合并了內(nèi)存屏障來(lái)確保在進(jìn)入臨界區(qū)之前它前面的加載和存儲(chǔ)指令都已經(jīng)完成。

關(guān)于如何使用鎖的信息,參閱”使用鎖”部分。

1.4 條件

條件是信號(hào)量的另外一個(gè)形式,它允許在條件為真的時(shí)候線程間互相發(fā)送信號(hào)。條件通常被使用來(lái)說(shuō)明資源可用性,或用來(lái)確保任務(wù)以特定的順序執(zhí)行。當(dāng)一個(gè)線程測(cè)試一個(gè)條件時(shí),它會(huì)被阻塞直到條件為真。它會(huì)一直阻塞直到其他線程顯式的修改信號(hào)量的狀態(tài)。條件和互斥鎖(mutex lock)的區(qū)別在于多個(gè)線程被允許同時(shí)訪問(wèn)一個(gè)條件。條件更多是允許不同線程根據(jù)一些指定的標(biāo)準(zhǔn)通過(guò)的守門人。

一個(gè)方式是你使用條件來(lái)管理掛起事件的池。事件隊(duì)列可能使用條件變量來(lái)給等待線程發(fā)送信號(hào),此時(shí)它們?cè)谑录?duì)列中的時(shí)候。如果一個(gè)事件到達(dá)時(shí),隊(duì)列將給條件發(fā)送合適信號(hào)。如果一個(gè)線程已經(jīng)處于等待,它會(huì)被喚醒,屆時(shí)它將會(huì)取出事件并處理它。如果兩個(gè)事件到達(dá)隊(duì)列的時(shí)間大致相同,隊(duì)列將會(huì)發(fā)送兩次信號(hào)喚醒兩個(gè)線程。

系統(tǒng)通過(guò)幾個(gè)不同的技術(shù)來(lái)支持條件。然而正確實(shí)現(xiàn)條件需要仔細(xì)編寫代碼,因此你應(yīng)該在你自己代碼中使用條件之前查看”使用條件”部分的例子。

1.5 執(zhí)行Selector例程

Cocoa程序包含了一個(gè)在一個(gè)線程以同步的方式傳遞消息的方便方法。NSObject類聲明方法來(lái)在應(yīng)用的一個(gè)活動(dòng)線程上面執(zhí)行selector的方法。這些方法允許你的線程以異步的方式來(lái)傳遞消息,以確保它們?cè)谕粋€(gè)線程上面執(zhí)行是同步的。比如,你可以通過(guò)執(zhí)行selector消息來(lái)把一個(gè)從你分布計(jì)算的結(jié)果傳遞給你的應(yīng)用的主線程或其他目標(biāo)線程。每個(gè)執(zhí)行selector的請(qǐng)求都會(huì)被放入一個(gè)目標(biāo)線程的run loop的隊(duì)列里面,然后請(qǐng)求會(huì)按照它們到達(dá)的順序被目標(biāo)線程有序的處理。

關(guān)于執(zhí)行selector例程的總結(jié)和更多關(guān)于如何使用它們的信息,參閱Cocoa執(zhí)行Selector源。

2.同步的成本和性能

同步幫助確保你代碼的正確性,但同時(shí)將會(huì)犧牲部分性能。甚至在無(wú)爭(zhēng)議的情況下,同步工具的使用將在后面介紹。鎖和原子操作通常包含了內(nèi)存屏障和內(nèi)核級(jí)別同步的使用來(lái)確保代碼正確被保護(hù)。如果,發(fā)生鎖的爭(zhēng)奪,你的線程有可能進(jìn)入阻塞,在體驗(yàn)上會(huì)產(chǎn)生更大的遲延。

表4-2列出了在無(wú)爭(zhēng)議情況下使用互斥鎖和原子操作的近似的相關(guān)成本。這些測(cè)試的平均值是使用了上千的樣本分析出的結(jié)果。隨著線程創(chuàng)建時(shí)間的推移,互斥采集時(shí)間(即使在無(wú)爭(zhēng)議情況下)可能相差也很大,這依賴于進(jìn)程的加載,計(jì)算機(jī)的處理速度和系統(tǒng)和程序現(xiàn)有可用的內(nèi)存。

Table 4-2  Mutex and atomic operation costs

Item

Approximate cost

Notes

Mutex acquisition time

Approximately 0.2 microseconds
[0.2微秒]

This is the lock acquisition time in an uncontested case. If the lock is held by another thread, the acquisition time can be much greater. The figures were determined by analyzing the mean and median values generated during mutex acquisition on an Intel-based iMac with a 2 GHz Core Duo processor and 1 GB of RAM running Mac OS X v10.5.

Atomic compare-and-swap

Approximately 0.05 microseconds
[0.05微秒]

This is the compare-and-swap time in an uncontested case. The figures were determined by analyzing the mean and median values for the operation and were generated on an Intel-based iMac with a 2 GHz Core Duo processor and 1 GB of RAM running Mac OS X v10.5.

當(dāng)設(shè)計(jì)你的并發(fā)任務(wù)時(shí),正確性是最重要的因素,但是也要考慮性能因素。代碼在多個(gè)線程下面正確執(zhí)行,但比相同代碼在當(dāng)線程執(zhí)行慢,這是難以改善的。如果你是改造已有的單線程應(yīng)用,你應(yīng)該始終給關(guān)鍵任務(wù)的性能設(shè)置測(cè)量基線。當(dāng)增加額外線程后,對(duì)相同的任務(wù)你應(yīng)該采取新的測(cè)量方法并比較多線程和單線程情況下的性能狀況。在改變代碼之后,線程并沒有提高性能,你應(yīng)該需要重新考慮具體的實(shí)現(xiàn)或同時(shí)使用線程。

關(guān)于性能的信息和收集指標(biāo)的工具,參閱Performance Overview。關(guān)于鎖原子成本的特定信息,參閱”線程成本”部分。

3.線程安全和信號(hào)量

當(dāng)涉及到多線程應(yīng)用程序時(shí),沒有什么比處理信號(hào)量更令人恐懼和困惑的了。信號(hào)量是底層BSD機(jī)制,它可以用來(lái)傳遞信息給進(jìn)程或以某種方式操縱它。一些應(yīng)用程序使用信號(hào)量來(lái)檢測(cè)特定事件,比如子進(jìn)程的消亡。系統(tǒng)使用信號(hào)量來(lái)終止失控進(jìn)程,和作為其他類型的通信消息。

使用信號(hào)量的問(wèn)題并不是你要做什么,而是當(dāng)你程序是多線程的時(shí)候它們的行為。在當(dāng)線程應(yīng)用程序里面,所有的信號(hào)量處理都在主線程進(jìn)行。在多線程應(yīng)用程序里面,信號(hào)量被傳遞到恰好運(yùn)行的線程,而不依賴于特定的硬件錯(cuò)誤(比如非法指令)。如果多個(gè)線程同時(shí)運(yùn)行,信號(hào)量被傳遞到任何一個(gè)系統(tǒng)挑選的線程。換而言之,信號(hào)量可以傳遞給你應(yīng)用的任何線程。

在你應(yīng)用程序里面實(shí)現(xiàn)信號(hào)量處理的第一條規(guī)則是避免假設(shè)任一線程處理信號(hào)量。如果一個(gè)指定的線程想要處理給定的信號(hào),你需要通過(guò)某些方法來(lái)通知該線程信號(hào)何時(shí)到達(dá)。你不能只是假設(shè)該線程的一個(gè)信號(hào)處理例程的安裝會(huì)導(dǎo)致信號(hào)被傳遞到同一線程里面。

關(guān)于更多信號(hào)量的信息和信號(hào)量處理例程的安裝信息,參見signal和sigaction主頁(yè)。

4.線程安全設(shè)計(jì)的技巧

同步工具是讓你代碼安全的有用方法,但是它們并非靈丹妙藥。使用太多鎖和其他同步的類型原語(yǔ)和非多線程相比明顯會(huì)降低你應(yīng)用的線程性能。在性能和安全之間尋找平衡是一門需要經(jīng)驗(yàn)的藝術(shù)。以下各部分提供幫助你為你應(yīng)用選擇合適的同步級(jí)別的技巧。

4.1 完全避免同步

對(duì)于你新的項(xiàng)目,甚至已有項(xiàng)目,設(shè)計(jì)你的代碼和數(shù)據(jù)結(jié)構(gòu)來(lái)避免使用同步是一個(gè)很好的解決辦法。雖然鎖和其他類型同步工具很有用,但是它們會(huì)影響任何應(yīng)用的性能。而且如果整體設(shè)計(jì)導(dǎo)致特定資源的高競(jìng)爭(zhēng),你的線程可能需要等待更長(zhǎng)時(shí)間。

實(shí)現(xiàn)并發(fā)最好的方法是減少你并發(fā)任務(wù)之間的交互和相互依賴。如果每個(gè)任務(wù)在它自己的數(shù)據(jù)集上面操作,那它不需要使用鎖來(lái)保護(hù)這些數(shù)據(jù)。甚至如果兩個(gè)任務(wù)共享一個(gè)普通數(shù)據(jù)集,你可以查看分區(qū)方法,它們?cè)O(shè)置或提供拷貝每一項(xiàng)任務(wù)的方法。當(dāng)然,拷貝數(shù)據(jù)集本身也需要成本,所以在你做出決定前,你需要權(quán)衡這些成本和使用同步工具造成的成本那個(gè)更可以接受。

4.2 了解同步的限制

同步工具只有當(dāng)它們被用在應(yīng)用程序中的所有線程是一致時(shí)才是有效的。如果你創(chuàng)建了互斥鎖來(lái)限制特定資源的訪問(wèn),你所有線程都必須在試圖操縱資源前獲得同一互斥鎖。如果不這樣做導(dǎo)致破壞一個(gè)互斥鎖提供的保護(hù),這是編程的錯(cuò)誤。

4.3 注意對(duì)代碼正確性的威脅

當(dāng)你使用鎖和內(nèi)存屏障時(shí),你應(yīng)該總是小心的把它們放在你代碼正確的地方。即使有條件的鎖(似乎很好放置)也可能會(huì)讓你產(chǎn)生一個(gè)虛假的安全感。以下一系列例子試圖通過(guò)指出看似無(wú)害的代碼的漏洞來(lái)舉例說(shuō)明該問(wèn)題。其基本前提是你有一個(gè)可變的數(shù)組,它包含一組不可變的對(duì)象集。假設(shè)你想要調(diào)用數(shù)組中第一個(gè)對(duì)象的方法。你可能會(huì)做類似下面那樣的代碼:

  1. NSLock* arrayLock = GetArrayLock(); 
  2. NSMutableArray* myArray = GetSharedArray(); 
  3. id anObject; 
  4.   
  5. [arrayLock lock]; 
  6. anObject = [myArray objectAtIndex:0]; 
  7. [arrayLock unlock]; 
  8.   
  9. [anObject doSomething];  

因?yàn)閿?shù)組是可變的,所有數(shù)組周圍的鎖防止其他線程修改該數(shù)組直到你獲得了想要的對(duì)象。而且因?yàn)閷?duì)象限制它們本身是不可更改的,所以在調(diào)用對(duì)象的doSomething方法周圍不需要鎖。

但是上面顯式的例子有一個(gè)問(wèn)題。如果當(dāng)你釋放該鎖,而在你有機(jī)會(huì)執(zhí)行doSomething方法前其他線程到來(lái)并從數(shù)組中刪除所有對(duì)象,那會(huì)發(fā)生什么呢?對(duì)于沒有使用垃圾回收的應(yīng)用程序,你代碼用戶的對(duì)象可能已經(jīng)釋放了,讓anObject對(duì)象指向一個(gè)非法的內(nèi)存地址。了修正該問(wèn)題,你可能決定簡(jiǎn)單的重新安排你的代碼,讓它在調(diào)用doSomething之后才釋放鎖,如下所示:

  1. NSLock* arrayLock = GetArrayLock(); 
  2. NSMutableArray* myArray = GetSharedArray(); 
  3. id anObject; 
  4.   
  5. [arrayLock lock]; 
  6. anObject = [myArray objectAtIndex:0]; 
  7. [anObject doSomething]; 
  8. [arrayLock unlock]; 

通過(guò)把doSomething的調(diào)用移到鎖的內(nèi)部,你的代碼可以保證該方法被調(diào)用的時(shí)候該對(duì)象還是有效的。不幸的是,如果doSomething方法需要耗費(fèi)很長(zhǎng)的時(shí)間,這有可能導(dǎo)致你的代碼保持擁有該鎖很長(zhǎng)時(shí)間,這會(huì)產(chǎn)生一個(gè)性能瓶頸。

該代碼的問(wèn)題不是關(guān)鍵區(qū)域定義不清,而是實(shí)際問(wèn)題是不可理解的。真正的問(wèn)題是由其他線程引發(fā)的內(nèi)存管理的問(wèn)題。因?yàn)樗梢员黄渌€程釋放,最好的解決辦法是在釋放鎖之前retain anObject。該解決方案涉及對(duì)象被釋放,并沒有引發(fā)一個(gè)強(qiáng)制的性能損失。

  1. NSLock* arrayLock = GetArrayLock(); 
  2. NSMutableArray* myArray = GetSharedArray(); 
  3. id anObject; 
  4.   
  5. [arrayLock lock]; 
  6. anObject = [myArray objectAtIndex:0]; 
  7. [anObject retain]; 
  8. [arrayLock unlock]; 
  9.   
  10. [anObject doSomething]; 
  11. [anObject release]; 

盡管前面的例子非常簡(jiǎn)單,它們說(shuō)明了非常重要的一點(diǎn)。當(dāng)它涉及到正確性時(shí),你需要考慮不僅僅是問(wèn)題的表面。內(nèi)存管理和其他影響你設(shè)計(jì)的因子都有可能因?yàn)槌霈F(xiàn)多個(gè)線程而受到影響,所以你必須考慮從上到下考慮這些問(wèn)題。此外,你應(yīng)該在涉及安全的時(shí)候假設(shè)編譯器總是出現(xiàn)最壞的情況。這種意識(shí)和警惕性,可以幫你避免潛在的問(wèn)題,并確保你的代碼運(yùn)行正確。

關(guān)于更多介紹如何讓你應(yīng)用程序安全的額外例子,參閱Technical Note TN2059:”Using Collection Classes Safely in Multithreaded Application”。

4.4 當(dāng)心死鎖(Deadlocks)和活鎖(Livelocks)

任何時(shí)候線程試圖同時(shí)獲得多于一個(gè)鎖,都有可能引發(fā)潛在的死鎖。當(dāng)兩個(gè)不同的線程分別保持一個(gè)鎖(而該鎖是另外一個(gè)線程需要的)又試圖獲得另外線程保持的鎖時(shí)就會(huì)發(fā)生死鎖。結(jié)果是每個(gè)線程都會(huì)進(jìn)入持久性阻塞狀態(tài),因?yàn)樗肋h(yuǎn)不可能獲得另外那個(gè)鎖。

一個(gè)活鎖和死鎖類似,當(dāng)兩個(gè)線程競(jìng)爭(zhēng)同一個(gè)資源的時(shí)候就可能發(fā)生活鎖。在發(fā)生活鎖的情況里,一個(gè)線程放棄它的第一個(gè)鎖并試圖獲得第二個(gè)鎖。一旦它獲得第二個(gè)鎖,它返回并試圖再次獲得一個(gè)鎖。線程就會(huì)被鎖起來(lái),因?yàn)樗ㄙM(fèi)所有的時(shí)間來(lái)釋放一個(gè)鎖,并試圖獲取其他鎖,而不做實(shí)際的工作。

避免死鎖和活鎖的最好方法是同一個(gè)時(shí)間只擁有一個(gè)鎖。如果你必須在同一時(shí)間獲取多于一個(gè)鎖,你應(yīng)該確保其他線程沒有做類似的事情。

4.5 正確使用Volatile變量

如果你已經(jīng)使用了一個(gè)互斥鎖來(lái)保護(hù)一個(gè)代碼段,不要自動(dòng)假設(shè)你需要使用關(guān)鍵詞volatile來(lái)保護(hù)該代碼段的重要的變量。一個(gè)互斥鎖包含了內(nèi)存屏障來(lái)確保加載和存儲(chǔ)操作是按照正確順序的。在一個(gè)臨界區(qū)添加關(guān)鍵字volatile到變量上面會(huì)強(qiáng)制每次訪問(wèn)該變量的時(shí)候都要從內(nèi)存里面從加載。這兩種同步技巧的組合使用在一些特定區(qū)域是必須的,但是同樣會(huì)導(dǎo)致顯著的性能損失。如果單獨(dú)使用互斥鎖已經(jīng)可以保護(hù)變量,那么忽略關(guān)鍵字volatile。

為了避免使用互斥鎖而不使用volatile變量同樣很重要。通常情況下,互斥鎖和其他同步機(jī)制是比volatile變量更好的方式來(lái)保護(hù)數(shù)據(jù)結(jié)構(gòu)的完整性。關(guān)鍵字volatile只是確保從內(nèi)存加載變量而不是使用寄存器里面的變量。它不保證你代碼訪問(wèn)變量是正確的。

別走開,下頁(yè)內(nèi)容更精彩

#p#

5.使用原子操作

非阻塞同步的方式是用來(lái)執(zhí)行某些類型的操作而避免擴(kuò)展使用鎖。盡管鎖是同步兩個(gè)線程的很好方式,獲取一個(gè)鎖是一個(gè)很昂貴的操作,即使在無(wú)競(jìng)爭(zhēng)的狀態(tài)下。相比,許多原子操作花費(fèi)很少的時(shí)間來(lái)完成操作也可以達(dá)到和鎖一樣的效果。

原子操作可以讓你在32位或64位的處理器上面執(zhí)行簡(jiǎn)單的數(shù)學(xué)和邏輯的運(yùn)算操作。這些操作依賴于特定的硬件設(shè)施(和可選的內(nèi)存屏障)來(lái)保證給定的操作在影響內(nèi)存再次訪問(wèn)的時(shí)候已經(jīng)完成。在多線程情況下,你應(yīng)該總是使用原子操作,它和內(nèi)存屏障組合使用來(lái)保證多個(gè)線程間正確的同步內(nèi)存。

表4-3列出了可用的原子運(yùn)算和本地操作和相應(yīng)的函數(shù)名。這些函數(shù)聲明在/usr/include/libkern/OSAtomic.h頭文件里面,在那里你也可以找到完整的語(yǔ)法。這些函數(shù)的64-位版本只能在64位的進(jìn)程里面使用。

Table 4-3  Atomic math and logic operations

Operation

Function name

Description

Add

OSAtomicAdd32
OSAtomicAdd32Barrier
OSAtomicAdd64
OSAtomicAdd64Barrier

Adds two integer values together and stores the result in one of the specified variables.

Increment

OSAtomicIncrement32
OSAtomicIncrement32Barrier
OSAtomicIncrement64
OSAtomicIncrement64Barrier

Increments the specified integer value by 1.

Decrement

OSAtomicDecrement32
OSAtomicDecrement32Barrier
OSAtomicDecrement64
OSAtomicDecrement64Barrier

Decrements the specified integer value by 1.

Logical OR

OSAtomicOr32
OSAtomicOr32Barrier

Performs a logical OR between the specified 32-bit value and a 32-bit mask.

Logical AND

OSAtomicAnd32
OSAtomicAnd32Barrier

Performs a logical AND between the specified 32-bit value and a 32-bit mask.

Logical XOR

OSAtomicXor32
OSAtomicXor32Barrier

Performs a logical XOR between the specified 32-bit value and a 32-bit mask.

Compare and swap

OSAtomicCompareAndSwap32
OSAtomicCompareAndSwap32Barrier
OSAtomicCompareAndSwap64
OSAtomicCompareAndSwap64Barrier
OSAtomicCompareAndSwapPtr
OSAtomicCompareAndSwapPtrBarrier
OSAtomicCompareAndSwapInt
OSAtomicCompareAndSwapIntBarrier
OSAtomicCompareAndSwapLong
OSAtomicCompareAndSwapLongBarrier

Compares a variable against the specified old value. If the two values are equal, this function assigns the specified new value to the variable; otherwise, it does nothing. The comparison and assignment are done as one atomic operation and the function returns a Boolean value indicating whether the swap actually occurred.

Test and set

OSAtomicTestAndSet
OSAtomicTestAndSetBarrier

Tests a bit in the specified variable, sets that bit to 1, and returns the value of the old bit as a Boolean value. Bits are tested according to the formula (0×80 >> (n & 7)) of byte((char*)address + (n >> 3)) where n is the bit number and address is a pointer to the variable. This formula effectively breaks up the variable into 8-bit sized chunks and orders the bits in each chunk in reverse. For example, to test the lowest-order bit (bit 0) of a 32-bit integer, you would actually specify 7 for the bit number; similarly, to test the highest order bit (bit 32), you would specify 24 for the bit number.

Test and clear

OSAtomicTestAndClear
OSAtomicTestAndClearBarrier

Tests a bit in the specified variable, sets that bit to 0, and returns the value of the old bit as a Boolean value. Bits are tested according to the formula (0×80 >> (n & 7)) of byte((char*)address + (n >> 3)) where n is the bit number and address is a pointer to the variable. This formula effectively breaks up the variable into 8-bit sized chunks and orders the bits in each chunk in reverse. For example, to test the lowest-order bit (bit 0) of a 32-bit integer, you would actually specify 7 for the bit number; similarly, to test the highest order bit (bit 32), you would specify 24 for the bit number.

 

大部分原子函數(shù)的行為是相對(duì)簡(jiǎn)單的并應(yīng)該是你想要的。然而列表4-1顯式了測(cè)試-設(shè)置和比較-交換操作的原子行為,它們相對(duì)復(fù)雜一點(diǎn)。OSAtomicTestAndSet 第一次調(diào)用展示了如何對(duì)一個(gè)整形值進(jìn)行位運(yùn)算操作,而它的結(jié)果和你預(yù)期的有差異。最后兩次調(diào)用OSAtomicCompareAndSwap32顯式它的行為。所有情況下,這些函數(shù)都是無(wú)競(jìng)爭(zhēng)的下調(diào)用的,此時(shí)沒有其他線程試圖操作這些值。

Listing 4-1  Performing atomic operations

  1. int32_t  theValue = 0; 
  2. OSAtomicTestAndSet(0, &theValue); 
  3. // theValue is now 128. 
  4.   
  5. theValue = 0; 
  6. OSAtomicTestAndSet(7, &theValue); 
  7. // theValue is now 1. 
  8.   
  9. theValue = 0; 
  10. OSAtomicTestAndSet(15, &theValue) 
  11. // theValue is now 256. 
  12.   
  13. OSAtomicCompareAndSwap32(256, 512, &theValue); 
  14. // theValue is now 512. 
  15.   
  16. OSAtomicCompareAndSwap32(256, 1024, &theValue); 
  17. // theValue is still 512. 

關(guān)于原子操作的更多信息,參見atomic的主頁(yè)和/usr/include/libkern/OSAtomic.h頭文件。

6.使用鎖

鎖是線程編程同步工具的基礎(chǔ)。鎖可以讓你很容易保護(hù)代碼中一大塊區(qū)域以便你可以確保代碼的正確性。Mac OS X和iOS都位所有類型的應(yīng)用程序提供了互斥鎖,而Foundation框架定義一些特殊情況下互斥鎖的額外變種。以下個(gè)部分顯式了如何使用這些鎖的類型。

6.1 使用POSIX互斥鎖

POSIX互斥鎖在很多程序里面很容易使用。為了新建一個(gè)互斥鎖,你聲明并初始化一個(gè)pthread_mutex_t的結(jié)構(gòu)。為了鎖住和解鎖一個(gè)互斥鎖,你可以使用pthread_mutex_lock和pthread_mutex_unlock函數(shù)。列表4-2顯式了要初始化并使用一個(gè)POSIX線程的互斥鎖的基礎(chǔ)代碼。當(dāng)你用完一個(gè)鎖之后,只要簡(jiǎn)單的調(diào)用pthread_mutex_destroy來(lái)釋放該鎖的數(shù)據(jù)結(jié)構(gòu)。

Listing 4-2  Using a mutex lock

  1. pthread_mutex_t mutex; 
  2. void MyInitFunction() 
  3.     pthread_mutex_init(&mutex, NULL); 
  4.   
  5. void MyLockingFunction() 
  6.     pthread_mutex_lock(&mutex); 
  7.     // Do work. 
  8.     pthread_mutex_unlock(&mutex); 

注意:上面的代碼只是簡(jiǎn)單的顯式了使用一個(gè)POSIX線程互斥鎖的步驟。你自己的代碼應(yīng)該檢查這些函數(shù)返回的錯(cuò)誤碼,并適當(dāng)?shù)奶幚硭鼈儭?/p>

6.2 使用NSLock類

在Cocoa程序中NSLock中實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的互斥鎖。所有鎖(包括NSLock)的接口實(shí)際上都是通過(guò)NSLocking協(xié)議定義的,它定義了lock和unlock方法。你使用這些方法來(lái)獲取和釋放該鎖。

除了標(biāo)準(zhǔn)的鎖行為,NSLock類還增加了tryLock和lockBeforeDate:方法。方法tryLock試圖獲取一個(gè)鎖,但是如果鎖不可用的時(shí)候,它不會(huì)阻塞線程。相反,它只是返回NO。而lockBeforeDate:方法試圖獲取一個(gè)鎖,但是如果鎖沒有在規(guī)定的時(shí)間內(nèi)被獲得,它會(huì)讓線程從阻塞狀態(tài)變?yōu)榉亲枞麪顟B(tài)(或者返回NO)。

下面的例子顯式了你可以是NSLock對(duì)象來(lái)協(xié)助更新一個(gè)可視化顯式,它的數(shù)據(jù)結(jié)構(gòu)被多個(gè)線程計(jì)算。如果線程沒有立即獲的鎖,它只是簡(jiǎn)單的繼續(xù)計(jì)算直到它可以獲得鎖再更新顯式。 

6.3 使用@synchronized指令

@synchronized指令是在Objective-C代碼中創(chuàng)建一個(gè)互斥鎖非常方便的方法。@synchronized指令做和其他互斥鎖一樣的工作(它防止不同的線程在同一時(shí)間獲取同一個(gè)鎖)。然而在這種情況下,你不需要直接創(chuàng)建一個(gè)互斥鎖或鎖對(duì)象。相反,你只需要簡(jiǎn)單的使用Objective-C對(duì)象作為鎖的令牌,如下面例子所示:

  1. - (void)myMethod:(id)anObj 
  2.     @synchronized(anObj) 
  3.     { 
  4.         // Everything between the braces is protected by the @synchronized directive. 
  5.     } 

創(chuàng)建給@synchronized指令的對(duì)象是一個(gè)用來(lái)區(qū)別保護(hù)塊的唯一標(biāo)示符。如果你在兩個(gè)不同的線程里面執(zhí)行上述方法,每次在一個(gè)線程傳遞了一個(gè)不同的對(duì)象給anObj參數(shù),那么每次都將會(huì)擁有它的鎖,并持續(xù)處理,中間不被其他線程阻塞。然而,如果你傳遞的是同一個(gè)對(duì)象,那么多個(gè)線程中的一個(gè)線程會(huì)首先獲得該鎖,而其他線程將會(huì)被阻塞直到第一個(gè)線程完成它的臨界區(qū)。

作為一種預(yù)防措施,@synchronized塊隱式的添加一個(gè)異常處理例程來(lái)保護(hù)代碼。該處理例程會(huì)在異常拋出的時(shí)候自動(dòng)的釋放互斥鎖。這意味著為了使用@synchronized指令,你必須在你的代碼中啟用異常處理。了如果你不想讓隱式的異常處理例程帶來(lái)額外的開銷,你應(yīng)該考慮使用鎖的類。

關(guān)于更多@synchronized指令的信息,參閱The Objective-C Programming Language。

6.4 使用其他Cocoa鎖

以下個(gè)部分描述了使用Cocoa其他類型的鎖。

使用NSRecursiveLock對(duì)象

NSRecursiveLock類定義的鎖可以在同一線程多次獲得,而不會(huì)造成死鎖。一個(gè)遞歸鎖會(huì)跟蹤它被多少次成功獲得了。每次成功的獲得該鎖都必須平衡調(diào)用鎖住和解鎖的操作。只有所有的鎖住和解鎖操作都平衡的時(shí)候,鎖才真正被釋放給其他線程獲得。

正如它名字所言,這種類型的鎖通常被用在一個(gè)遞歸函數(shù)里面來(lái)防止遞歸造成阻塞線程。你可以類似的在非遞歸的情況下使用他來(lái)調(diào)用函數(shù),這些函數(shù)的語(yǔ)義要求它們使用鎖。以下是一個(gè)簡(jiǎn)單遞歸函數(shù),它在遞歸中獲取鎖。如果你不在該代碼里使用NSRecursiveLock對(duì)象,當(dāng)函數(shù)被再次調(diào)用的時(shí)候線程將會(huì)出現(xiàn)死鎖。

  1. NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init]; 
  2. void MyRecursiveFunction(int value) 
  3.     [theLock lock]; 
  4.     if (value != 0) 
  5.     { 
  6.         --value; 
  7.         MyRecursiveFunction(value); 
  8.     } 
  9.     [theLock unlock]; 
  10. MyRecursiveFunction(5); 

注意:因?yàn)橐粋€(gè)遞歸鎖不會(huì)被釋放直到所有鎖的調(diào)用平衡使用了解鎖操作,所以你必須仔細(xì)權(quán)衡是否決定使用鎖對(duì)性能的潛在影響。長(zhǎng)時(shí)間持有一個(gè)鎖將會(huì)導(dǎo)致其他線程阻塞直到遞歸完成。如果你可以重寫你的代碼來(lái)消除遞歸或消除使用一個(gè)遞歸鎖,你可能會(huì)獲得更好的性能。

使用NSConditionLock對(duì)象

NSConditionLock對(duì)象定義了一個(gè)互斥鎖,可以使用特定值來(lái)鎖住和解鎖。不要把該類型的鎖和條件(參見“條件”部分)混淆了。它的行為和條件有點(diǎn)類似,但是它們的實(shí)現(xiàn)非常不同。

通常,當(dāng)多線程需要以特定的順序來(lái)執(zhí)行任務(wù)的時(shí)候,你可以使用一個(gè)NSConditionLock對(duì)象,比如當(dāng)一個(gè)線程生產(chǎn)數(shù)據(jù),而另外一個(gè)線程消費(fèi)數(shù)據(jù)。生產(chǎn)者執(zhí)行時(shí),消費(fèi)者使用由你程序指定的條件來(lái)獲取鎖(條件本身是一個(gè)你定義的整形值)。當(dāng)生產(chǎn)者完成時(shí),它會(huì)解鎖該鎖并設(shè)置鎖的條件為合適的整形值來(lái)喚醒消費(fèi)者線程,之后消費(fèi)線程繼續(xù)處理數(shù)據(jù)。

NSConditionLock的鎖住和解鎖方法可以任意組合使用。比如,你可以使用unlockWithCondition:和lock消息,或使用lockWhenCondition:和unlock消息。當(dāng)然,后面的組合可以解鎖一個(gè)鎖但是可能沒有釋放任何等待某特定條件值的線程。

下面的例子顯示了生產(chǎn)者-消費(fèi)者問(wèn)題如何使用條件鎖來(lái)處理。想象一個(gè)應(yīng)用程序包含一個(gè)數(shù)據(jù)的隊(duì)列。一個(gè)生產(chǎn)者線程把數(shù)據(jù)添加到隊(duì)列,而消費(fèi)者線程從隊(duì)列中取出數(shù)據(jù)。生產(chǎn)者不需要等待特定的條件,但是它必須等待鎖可用以便它可以安全的把數(shù)據(jù)添加到隊(duì)列。

  1. id condLock = [[NSConditionLock alloc] initWithCondition:NO_DATA]; 
  2.   
  3. while(true
  4.     [condLock lock]; 
  5.     /* Add data to the queue. */ 
  6.     [condLock unlockWithCondition:HAS_DATA]; 

因?yàn)槌跏蓟瘲l件鎖的值為NO_DATA,生產(chǎn)者線程在初始化的時(shí)候可以毫無(wú)問(wèn)題的獲取該鎖。它會(huì)添加隊(duì)列數(shù)據(jù),并把條件設(shè)置為HAS_DATA。在隨后的迭代中,生產(chǎn)者線程可以把到達(dá)的數(shù)據(jù)添加到隊(duì)列,無(wú)論隊(duì)列是否為空或依然有數(shù)據(jù)。唯一讓它進(jìn)入阻塞的情況是當(dāng)一個(gè)消費(fèi)者線程充隊(duì)列取出數(shù)據(jù)的時(shí)候。

因?yàn)橄M(fèi)者線程必須要有數(shù)據(jù)來(lái)處理,它會(huì)使用一個(gè)特定的條件來(lái)等待隊(duì)列。當(dāng)生產(chǎn)者把數(shù)據(jù)放入隊(duì)列時(shí),消費(fèi)者線程被喚醒并獲取它的鎖。它可以從隊(duì)列中取出數(shù)據(jù),并更新隊(duì)列的狀態(tài)。下列代碼顯示了消費(fèi)者線程處理循環(huán)的基本結(jié)構(gòu)。

  1. while (true
  2.     [condLock lockWhenCondition:HAS_DATA]; 
  3.     /* Remove data from the queue. */ 
  4.     [condLock unlockWithCondition:(isEmpty ? NO_DATA : HAS_DATA)]; 
  5.   
  6.     // Process the data locally. 
  7. }

使用NSDistributedLock對(duì)象

NSDistributedLock類可以被多臺(tái)主機(jī)上的多個(gè)應(yīng)用程序使用來(lái)限制對(duì)某些共享資源的訪問(wèn),比如一個(gè)文件。鎖本身是一個(gè)高效的互斥鎖,它使用文件系統(tǒng)項(xiàng)目來(lái)實(shí)現(xiàn),比如一個(gè)文件或目錄。對(duì)于一個(gè)可用的NSDistributedLock對(duì)象,鎖必須由所有使用它的程序?qū)懭搿_@通常意味著把它放在文件系統(tǒng),該文件系統(tǒng)可以被所有運(yùn)行在計(jì)算機(jī)上面的應(yīng)用程序訪問(wèn)。

不像其他類型的鎖,NSDistributedLock并沒有實(shí)現(xiàn)NSLocking協(xié)議,所有它沒有l(wèi)ock方法。一個(gè)lock方法將會(huì)阻塞線程的執(zhí)行,并要求系統(tǒng)以預(yù)定的速度輪詢鎖。以其在你的代碼中實(shí)現(xiàn)這種約束,NSDistributedLock提供了一個(gè)tryLock方法,并讓你決定是否輪詢。

因?yàn)樗褂梦募到y(tǒng)來(lái)實(shí)現(xiàn),一個(gè)NSDistributedLock對(duì)象不會(huì)被釋放除非它的擁有者顯式的釋放它。如果你的程序在用戶一個(gè)分布鎖的時(shí)候崩潰了,其他客戶端簡(jiǎn)無(wú)法訪問(wèn)該受保護(hù)的資源。在這種情況下,你可以使用breadLock方法來(lái)打破現(xiàn)存的鎖以便你可以獲取它。但是通常應(yīng)該避免打破鎖,除非你確定擁有進(jìn)程已經(jīng)死亡并不可能再釋放該鎖。

和其他類型的鎖一樣,當(dāng)你使用NSDistributedLock對(duì)象時(shí),你可以通過(guò)調(diào)用unlock方法來(lái)釋放它。

別走開,下頁(yè)內(nèi)容更精彩

#p#

7.使用條件

條件是一個(gè)特殊類型的鎖,你可以使用它來(lái)同步操作必須處理的順序。它們和互斥鎖有微妙的不同。一個(gè)線程等待條件會(huì)一直處于阻塞狀態(tài)直到條件獲得其他線程顯式發(fā)出的信號(hào)。

由于微妙之處包含在操作系統(tǒng)實(shí)現(xiàn)上,條件鎖被允許返回偽成功,即使實(shí)際上它們并沒有被你的代碼告知。為了避免這些偽信號(hào)操作的問(wèn)題,你應(yīng)該總是在你的條件鎖里面使用一個(gè)斷言。該斷言是一個(gè)更好的方法來(lái)確定是否安全讓你的線程處理。條件簡(jiǎn)單的讓你的線程保持休眠直到斷言被發(fā)送信號(hào)的線程設(shè)置了。

以下部分介紹了如何在你的代碼中使用條件。

7.1 使用NSCondition類

NSCondition類提供了和POSIX條件相同的語(yǔ)義,但是它把鎖和條件數(shù)據(jù)結(jié)構(gòu)封裝在一個(gè)單一對(duì)象里面。結(jié)果是一個(gè)你可以像互斥鎖那樣使用的對(duì)象,然后等待特定條件。

列表4-3顯示了一個(gè)代碼片段,它展示了為等待一個(gè)NSCondition對(duì)象的事件序列。cocaoCondition變量包含了一個(gè)NSCondition對(duì)象,而timeToDoWork變量是一個(gè)整形,它在其他線程里面發(fā)送條件信號(hào)時(shí)立即遞增。

Listing 4-3  Using a Cocoa condition

  1. [cocoaCondition lock]; 
  2. while (timeToDoWork <= 0) 
  3.     [cocoaCondition wait]; 
  4.   
  5. timeToDoWork--; 
  6.   
  7. // Do real work here. 
  8.   
  9. [cocoaCondition unlock];

列表4-4顯示了用于給Cocoa條件發(fā)送信號(hào)的代碼,并遞增他斷言變量。你應(yīng)該在給它發(fā)送信號(hào)前鎖住條件。

Listing 4-4  Signaling a Cocoa condition

  1. [cocoaCondition lock]; 
  2. timeToDoWork++; 
  3. [cocoaCondition signal]; 
  4. [cocoaCondition unlock]; 

7.2 使用POSIX條件

POSIX線程條件鎖要求同時(shí)使用條件數(shù)據(jù)結(jié)構(gòu)和一個(gè)互斥鎖。經(jīng)管兩個(gè)鎖結(jié)構(gòu)是分開的,互斥鎖在運(yùn)行的時(shí)候和條件結(jié)構(gòu)緊密聯(lián)系在一起。多線程等待某一信號(hào)應(yīng)該總是一起使用相同的互斥鎖和條件結(jié)構(gòu)。修改該成雙結(jié)構(gòu)將會(huì)導(dǎo)致錯(cuò)誤。

列表4-5顯示了基本初始化過(guò)程,條件和斷言的使用。在初始化之后,條件和互斥鎖,使用ready_to_go變量作為斷言等待線程進(jìn)入一個(gè)while循環(huán)。僅當(dāng)斷言被設(shè)置并且隨后的條件信號(hào)等待線程被喚醒和開始工作。

Listing 4-5  Using a POSIX condition

  1. pthread_mutex_t mutex; 
  2. pthread_cond_t condition; 
  3. Boolean     ready_to_go = true
  4.   
  5. void MyCondInitFunction() 
  6.     pthread_mutex_init(&mutex); 
  7.     pthread_cond_init(&condition, NULL); 
  8.   
  9. void MyWaitOnConditionFunction() 
  10.     // Lock the mutex. 
  11.     pthread_mutex_lock(&mutex); 
  12.   
  13.     // If the predicate is already set, then the while loop is bypassed; 
  14.     // otherwise, the thread sleeps until the predicate is set. 
  15.     while(ready_to_go == false
  16.     { 
  17.         pthread_cond_wait(&condition, &mutex); 
  18.     } 
  19.   
  20.     // Do work. (The mutex should stay locked.) 
  21.   
  22.     // Reset the predicate and release the mutex. 
  23.     ready_to_go = false
  24.     pthread_mutex_unlock(&mutex); 

信號(hào)線程負(fù)責(zé)設(shè)置斷言和發(fā)送信號(hào)給條件鎖。列表4-6顯示了實(shí)現(xiàn)該行為的代碼。在該例子中,條件被互斥鎖內(nèi)被發(fā)送信號(hào)來(lái)防止等待條件的線程間發(fā)生競(jìng)爭(zhēng)條件。

Listing 4-6  Signaling a condition lock

  1. void SignalThreadUsingCondition() 
  2.     // At this point, there should be work for the other thread to do. 
  3.     pthread_mutex_lock(&mutex); 
  4.     ready_to_go = true
  5.   
  6.     // Signal the other thread to begin work. 
  7.     pthread_cond_signal(&condition); 
  8.   
  9.     pthread_mutex_unlock(&mutex); 

注意:上述代碼是顯示使用POSIX線程條件函數(shù)的簡(jiǎn)單例子。你自己的代碼應(yīng)該檢測(cè)這些函數(shù)返回錯(cuò)誤碼并恰當(dāng)?shù)奶幚硭鼈儭?/p>

責(zé)任編輯:閆佳明 來(lái)源: dreamingwish
相關(guān)推薦

2013-07-16 10:12:14

iOS多線程多線程概念多線程入門

2013-07-16 10:57:34

iOS多線程多線程概念多線程入門

2013-07-16 13:39:11

2013-07-16 11:38:46

iOS多線程多線程概念GCD

2015-07-22 09:51:51

iOS開發(fā)線程

2015-07-22 09:39:38

IOS多線程同步

2013-07-15 15:35:06

2010-01-21 11:27:30

linux多線程機(jī)制線程同步

2023-09-26 10:30:57

Linux編程

2009-03-12 10:52:43

Java線程多線程

2023-06-13 13:39:00

多線程異步編程

2023-07-27 08:59:19

線程同步Python

2013-06-07 16:30:08

iOS多線程iOS開發(fā)NSThread

2023-06-07 13:49:00

多線程編程C#

2023-06-05 07:56:10

線程分配處理器

2023-06-06 08:17:52

多線程編程Thread類

2009-09-14 19:39:14

批量線程同步

2011-06-13 10:41:17

JAVA

2023-04-02 17:53:10

多線程編程自測(cè)

2024-10-10 09:46:18

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)