2012-06-01 79 views
35

我有一個關於Objective-C中線程安全的問題。我已經閱讀了一些其他答案,一些Apple文檔,並且仍然對此有所懷疑,所以我想我會問自己的問題。鎖定一個對象被多個線程訪問 - Objective-C

我的問題是三折

假設我有一個數組,NSMutableArray *myAwesomeArray;

折1:

現在,糾正我,如果我錯了,但是從我的理解,使用@synchronized(myAwesomeArray){...}將阻止兩個線程訪問相同的代碼塊。所以,基本上,如果我有這樣的:

-(void)doSomething { 
    @synchronized(myAwesomeArray) { 
     //some read/write operation on myAwesomeArray 
    } 
} 

那麼,如果兩個線程在同一時間訪問相同方法,代碼塊將是線程安全的。我猜我已經正確理解了這個部分。

折2:

,該怎麼辦,如果myAwesomeArray正在被多個線程不同的方法來訪問? 如果我有這樣的:

- (void)readFromArrayAccessedByThreadOne { 
    //thread 1 reads from myAwesomeArray 
} 

- (void)writeToArrayAccessedByThreadTwo { 
    //thread 2 writes to myAwesomeArray 
} 

現在,這兩種方法是由兩個不同的線程在同一時間訪問。我如何確保myAwesomeArray不會有問題?我使用類似NSLock或NSRecursiveLock的東西嗎?

折3:

現在,在上述兩種情況下,myAwesomeArray是在存儲器中的實例變量。如果我有一個數據庫文件,我並不總是保存在內存中。每當我想執行數據庫操作時,我都會創建一個databaseManagerInstance,並在完成後釋放它。因此,基本上,不同的類可以訪問數據庫。每個類創建自己的實例DatabaseManger,但基本上它們都使用相同的單個數據庫文件。在這種情況下,如何確保數據不因爲競爭條件而損壞?

這將幫助我清除一些基本面。

+1

'@ synchronize'防止其它線程訪問已被鎖定相同的變量,而不是特定的代碼塊。 –

+0

啊。我懂了。好吧,我想還有一點我對@synchronize指令的理解。謝謝! :D – codeBearer

+0

@codeBearer我在http://stackoverflow.com/a/15393623/412916回答了你的問題。 – Jano

回答

41

摺疊1 通常您對@synchronized做的事情的理解是正確的。但是,從技術上講,它不會使任何代碼「線程安全」。它可以防止不同的線程同時獲得同一個鎖,但是您需要確保在執行關鍵部分時始終使用相同的同步令牌。如果你不這樣做,你仍然可以發現自己處於兩個線程同時執行關鍵部分的情況。 Check the docs

折2 大多數人可能會建議你使用NSRecursiveLock。如果我是你,我會使用GCD。 Here is a great document showing how to migrate from thread programming to GCD programming,我認爲這個問題的方法比基於NSLock的方法要好得多。簡而言之,您將創建一個串行隊列並將您的任務分派到該隊列中。通過這種方式,您可以確保您的關鍵部分是連續處理的,因此在任何特定時間只能執行一個關鍵部分。

折3 這僅僅是更具體的相同折2,。數據庫是一種資源,通過許多方式,它與數組或其他任何東西都是一樣的。 If you want to see the GCD based approach in database programming context, take a look at fmdb implementation。它正是我所描述的Fold2

作爲一個側面說明,以折3,我不認爲實例每次的DatabaseManager您要使用的數據庫,然後釋放它是正確的做法。我認爲你應該創建一個單一的數據庫連接,並通過你的應用程序會話保留它。這樣管理它就更容易了。同樣,fmdb就是如何實現這個目標的一個很好的例子。

編輯 如果不想使用GCD然後是,你將需要使用某種鎖定機制,是的,NSRecursiveLock將防止死鎖,如果你在你的方法用遞歸,所以這是一個很好的選擇(它由@synchronized使用)。但是,可能有一個捕獲。如果很多線程可能會等待相同的資源並且它們的訪問順序是相關的,那麼NSRecursiveLock是不夠的。您仍然可以用NSCondition來管理這種情況,但請相信我,在這種情況下,您將使用GCD節省大量時間。如果線程的順序不相關,則使用鎖定可以保證安全。

+0

謝謝你的回覆!我想這會照顧到我的大部分疑問。 就像一個確認:我猜我會爲摺疊3使用'NSRecursiveLock',如果我不使用GCD,對吧?這對我的知識基礎更爲重要,所以我的理念清晰明瞭。 此外,感謝您在_Fold 3_上的附註。我同意你的看法,最好是保留一個班級,並根據需要打開和關閉連接。我只是重寫了一些做了多個實例化的代碼 - 這是多麼噩夢。 >我已經提出了一個更好的圖畫。 :) – codeBearer

+0

高興地幫忙,看看我編輯的答案。 – lawicko

+0

完美!再次感謝! :D myKnowledge ++我一直計劃在所有新項目中開始使用GCD,並且您提供的鏈接是一個很好的起點! :) – codeBearer

1

子類NSMutableArray爲訪問器(讀取和寫入)方法提供鎖定。例如:

@interface MySafeMutableArray : NSMutableArray { NSRecursiveLock *lock; } @end 

@implementation MySafeMutableArray 

- (void)addObject:(id)obj { 
    [self.lock lock]; 
    [super addObject: obj]; 
    [self.lock unlock]; 
} 

// ... 
@end 

此方法將鎖定作爲數組的一部分進行封裝。用戶不需要改變他們的呼叫(但是可能需要知道,如果訪問時間緊迫,他們可以阻止/等待訪問)。這種方法的一個顯着優點是,如果您決定不使用鎖定,則可以重新實現MySafeMutableArray以使用分派隊列 - 或者最適合您的特定問題的任何方式。例如,你可以實現ADDOBJECT爲:

- (void)addObject:(id)obj { 
    dispatch_sync (self.queue, ^{ [super addObject: obj] }); 
} 

注意:如果使用鎖,你肯定會需要NSRecursiveLock,不NSLock,因爲你不知道ADDOBJECT的Objective-C的實現等本身遞歸。

+0

感謝您的回覆。如果我要創建一個對象的線程安全子類,這是一個好主意。 :) – codeBearer

+1

如果你將線程安全性留給調用者(也就是說,你沒有創建一個線程安全的子類,那麼如何實現),那麼你會重複的將自己設置爲問題。你會爲自己學習一些關於旋轉技術的知識。祝你好運。 – GoZoner

+0

太棒了。感謝您的提示。我會仔細看看的。 :D – codeBearer

相關問題