2011-09-08 173 views
4

我讀過應該使用GCD同步隊列(dispatch_sync)來實現代碼的關鍵部分。一個例子是從賬戶餘額中減去交易金額的塊。同步調用的有趣部分是一個問題,它是如何影響多線程上其他塊的工作的?是否在同步GCD隊列上放置一個阻止阻塞並暫停其他阻塞的阻塞?

讓我們想象一下,有3個線程在異步模式下使用並執行主隊列和自定義隊列中的系統和用戶定義塊。這些塊都按照某種順序並行執行。現在,如果一個塊放在一個具有同步模式的自定義隊列中,是否意味着所有其他塊(包括其他線程)都被掛起,直到成功執行塊爲止?或者這意味着只有一些鎖將被放在該塊上,而其他鎖仍然會執行。但是,如果其他塊使用與同步塊相同的數據,那麼其他塊將不可避免地等待,直到該鎖將被釋放。

恕我直言,這並不重要,它是一個或多個核心,同步模式應凍結整個應用程序的工作。但是,這些只是我的想法,所以請評論和分享你的見解:)

+0

沒有「同步隊列」。有同步調度(等待塊完成)和異步調度(只是將該塊添加到隊列中)。併發隊列和並行隊列。連續隊列不能用於_implement_關鍵部分,但它們對於避免關鍵部分的需要非常有用。 – gnasher729

回答

7

同步調度會暫停執行代碼,直到調度塊完成。立即派遣異步回報,該塊相對於調用代碼異步執行:

dispatch_sync(somewhere, ^{ something }); 
// Reached later, when the block is finished. 

dispatch_async(somewhere, ^{ something }); 
// Reached immediately. The block might be waiting 
// to be executed, executing or already finished. 

而且有兩種調度隊列,串行和並行的。序列號按照它們被添加的順序嚴格地逐個分派塊。當一個完成時,另一個開始。這種執行只需要一個線程。併發隊列同時並行地分派塊。那裏有更多的線程在使用。

如果您認爲合適,您可以混合使用sync/async dispatch和串行/併發隊列。如果您想要使用GCD來保護對關鍵部分的訪問,請使用單個串行隊列並分配此隊列上共享數據的所有操作(同步或異步,無關緊要)。這樣,總會有僅一個街區與共享數據進行操作:

- (void) addFoo: (id) foo { 
    dispatch_sync(guardingQueue, ^{ [sharedFooArray addObject:foo]; }); 
} 

- (void) removeFoo: (id) foo { 
    dispatch_sync(guardingQueue, ^{ [sharedFooArray removeObject:foo]; }); 
} 

現在,如果guardingQueue是一個串行隊列,添加/刪除操作不能發生衝突,即使addFoo:removeFoo:方法都是從不同的同時調用線程。

+0

那麼兩個塊(一個asyn A和另一個同步B)在不同線程上執行但兩個塊使用同一個對象(例如具有餘額的帳戶)的情況如何呢?如果同步B塊在中間時間線開始並更新餘額,而A塊嘗試讀取帳戶,那麼是否會出現競賽?我們是否明確地做了一些事情來阻止塊A讀取數據? – Centurion

+0

據我瞭解你的問題,使用串行隊列和調度同步模式塊只是凍結調用者代碼,直到塊完成。沒有其他的保護措施來鎖定共享資源的讀/寫。 – Centurion

+0

談論同步/異步塊或隊列是沒有意義的。同步或異步就是您在隊列中分派塊的方式。我已經更新了答案,以解釋如何保護GCD的關鍵部分,希望有所幫助。 – zoul

2

不,它不。

同步的部分是塊放在隊列中,但控制不返回調用函數,直到塊返回。

GCD的許多用途是異步的;你把一個塊放在一個隊列中,而不是等待塊完成它的工作控制傳回給調用函數。

這對其他隊列沒有影響。

1

如果您需要序列化某個資源的訪問權限,那麼您至少可以訪問兩個機制。如果你有一個帳戶對象(即對於給定的帳號獨特 ),那麼你可以這樣做:

@synchronize(accountObject) { ... } 

如果你沒有一個對象,但使用C結構,其中有隻有一個 對於給定的帳號這樣的結構,那麼你可以做到以下幾點:

// Should be added to the account structure. 
// 1 => at most 1 object can access accountLock at a time. 
dispatch_semaphore_t accountLock = dispatch_semaphore_create(1); 

// In your block you do the following: 
block = ^(void) { 
    dispatch_semaphore_wait(accountLock,DISPATCH_TIME_FOREVER); 
    // Do something 
    dispatch_semaphore_signal(accountLock); 
}; 

// -- Edited: semaphore was leaking. 
// At the appropriate time release the lock 
// If the semaphore was created in the init then 
// the semaphore should be released in the release method. 
dispatch_release(accountLock); 

有了這個,不管你的隊列併發的水平,可以保證只有一個線程將隨時訪問帳戶給定的時間。

還有更多類型的同步對象,但這兩個很容易使用,並且相當靈活。

+0

正是我需要的!小型打字雖然。 'dispatch_semaphore_wait'有兩個參數。像'dispatch_semaphore_wait(accountLock,DISPATCH_TIME_FOREVER);'應該做的伎倆。 –

+0

該文檔說明您在完成信號量時應該「dispatch_release」。每次我都會崩潰。不知道我在做什麼。無論哪種方式,這個片段中的信號量都在泄漏。 –

+0

你是正確的信號量正在泄漏。你可能會崩潰,因爲你試圖在錯誤的時刻釋放信號量?假設鎖是通過對象創建的,它應該在釋放對象時釋放。我會在上面的代碼中添加註釋以達到這個效果。 – aLevelOfIndirection