一個典型的設置:我們有一個mainMOC
的主線程和一個自己的後臺線程backgroundMOC
。後臺線程通過將塊調度到backgroundQueue
來對backgroundMOC
執行只讀操作。如何在跨線程合併更改時防止競爭條件?
的backgroundMOC
需要合併從mainMOC
的變化,所以我們註冊NSManagedObjectContextDidSaveNotification
,然後像做
- (void)mainMocDidSave:(NSNotification *)notification {
dispatch_async(backgroundQueue, ^{
[backgroundMoc mergeChangesFromContextDidSaveNotification:notification];
});
}
比方說,用戶刪除的mainMOC
的對象。上面的代碼對我來說似乎並不安全,因爲合併將在未來的某個時間點完成。在合併完成之前,backgroundQueue
上的塊可能仍會嘗試使用已刪除的對象。
明顯的解決方案是使用dispatch_sync
代替(或performBlockAndWait
,performSelector:OnThread:...
)。從我在互聯網上看到的代碼片段來看,這似乎是每個人都在做的事情。但是我對這個解決方案也不舒服。
名稱NSManagedObjectContextDidSaveNotification
意味着保存已在通知發送時發生。所以相應的行已經從底層數據庫中刪除(假設一個sqlite存儲)。 dispatch_sync
必須等待隊列上的其他塊完成才能合併更改,而這些其他塊仍然可以嘗試使用已刪除的對象,從而產生NSObjectInaccessibleException
。
在我看來,要合併從一個線程/隊列到另一個變化的正確方法是
- 訂閱
NSManagedObjectContextWillSaveNotification
和NSManagedObjectContextDidSaveNotification
在後臺線程。 - 對
NSManagedObjectContextWillSaveNotification
:清空backgroundQueue
並暫停向隊列分派新塊的任何操作。 - 對
NSManagedObjectContextDidSaveNotification
:同步合併更改。 - 恢復對後臺隊列的正常操作。
這是正確的方法還是我錯過了什麼?
作爲後續:我不斷收到死鎖與上面的解決方案。看起來在某些情況下,主控線程在發送'NSManagedObjectContextWillSaveNotification'時已經獲得了PSC上的鎖定,這可能導致上面步驟2中所需的dispatch_sync調用在背景隊列上仍有未完成的任務時一直等待。 – Lukas 2013-03-26 12:48:28