2012-08-28 151 views
2

我正在通過新創建的NSManagedObjectContext在後臺線程(使用GCD)上執行一個代價高昂的獲取(約5秒,約30,000個對象)由後臺線程擁有。主線程在後臺線程執行時等待[NSManagedObjectContext(_NSInternalAdditions)lockObjectStore]

但是,我沒有得到在後臺執行此操作的好處,因爲主線程正在等待持久存儲上的鎖定並因此UI被凍結。這裏的堆棧跟蹤:

* thread #1: tid = 0x1c03, 0x3641be78 CoreData`-[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore], stop reason = breakpoint 1.1 
    frame #0: 0x3641be78 CoreData`-[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore] 
    frame #1: 0x36432f06 CoreData`-[_PFManagedObjectReferenceQueue _processReferenceQueue:] + 1754 
    frame #2: 0x36435fd6 CoreData`_performRunLoopAction + 202 
    frame #3: 0x35ab9b1a CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 18 
    frame #4: 0x35ab7d56 CoreFoundation`__CFRunLoopDoObservers + 258 
    frame #5: 0x35ab80b0 CoreFoundation`__CFRunLoopRun + 760 
    frame #6: 0x35a3b4a4 CoreFoundation`CFRunLoopRunSpecific + 300 
    frame #7: 0x35a3b36c CoreFoundation`CFRunLoopRunInMode + 104 
    frame #8: 0x376d7438 GraphicsServices`GSEventRunModal + 136 
    frame #9: 0x33547cd4 UIKit`UIApplicationMain + 1080 
    frame #10: 0x000f337a MyApp`main + 90 at main.m:16 

我(相信)我已經證實了我不是訪問主線程的NSManagedObjectContext而這個後臺線程正在做的工作。從堆棧跟蹤中,很明顯,我沒有直接對Core Data做任何事情。但是有一些事件觸發了對_processReferenceQueue的調用:這導致試圖鎖定商店。有沒有人碰巧知道這個方法做了什麼,以及如何防止它在我的後臺線程執行其工作時被調用?

編輯

我開球此背景下取後,我沒有做任何核心數據讀取或在主線程上寫道。這就是爲什麼這是如此令人費解。如果主線也在嘗試做更多的工作,我預計會有爭議,但這不是 - 至少,我沒有要求它。沒有讀,不寫,沒有FRC。這就是爲什麼我想知道是否有人熟悉這個_processReferenceQueue方法。爲什麼會被叫?我能做些什麼,導致它運行?

編輯

作爲測試,我試圖讓MT的MOC進入狀態,它沒有未決的改變之前,我設置了藍牙功能關閉做的獲取,在希望它不會需要在_processReferenceQueue中做任何需要商店鎖定的工作。

在開始BT之前,我注意到在[MOC updatedObjects]集合中有一個對象。插入或刪除的組中沒有對象。

在調用[MOC save]之後,[MOC updatedObjects]集合就像預期的那樣是空的。
但是,一旦我開始BT,MT仍然試圖鎖定店內_processReferenceQueue,儘管沒有什麼在MOC中應該是髒的。

我試過的下一件事(嚴格來說就是測試)是在開始BT之前調用[MOC reset]。如預期的那樣,[MOC updatedObjects]集合在重置之後也是空的。在代碼中的這一點上,我沒有觸及MT上的任何託管對象,直到BT完成其工作(所以我沒有遇到任何問題,因爲重置使我已經引用的託管對象無效)。然而,這一次,MT確實不是試圖鎖定_processReferenceQueue中的持久存儲。

這種行爲暗示了我在啓動BT後沒有對MT上的MOC進行任何明確的操作。否則,MT會在_processReferenceQueue內部或外部的某個點(讀取或寫入)請求鎖定。但事實並非如此。

我不確定爲什麼最近保存的MOC需要後續鎖定_processReferenceQueue而最近重置的MOC不需要。

我會繼續挖掘。

謝謝! 布賴恩

+0

我有一個想法,但今天通過回答一個缺乏信息的問題已經燒了兩次。請在後臺線程中發佈更多關於你在做什麼的信息,以及你在主線程中做什麼(例如,你是否在後臺執行任何寫操作,主線程中是否有FRC)。 ..等 –

+0

另外,請記住,您不能跨線程傳遞提取的對象。在後臺使用這些30k對象,或傳遞其ID。 –

+0

以上更新。此外,即使我僅僅在背景上執行提取並忽略結果(作爲測試),MT上的鎖定也會發生。我確實將objectID傳回MT。 – BrianJStafford

回答

2

沒有更多的信息,我所能做的就是猜測。

不幸的是,核心數據並未使用相同的持久存儲協調器爲MOC實現讀寫器鎖定。這意味着在一個線程上讀取會阻塞使用相同持久存儲協調器的其他線程。

因此,您在後臺長時間執行的讀取操作將阻止主線程讀取。

你有幾種選擇。

分解長時間運行的提取,以便按順序提取小塊。您想要執行多個小提取操作,而不是在完成當前操作之前發出下一個小提取操作。

這將允許在主線程上獲取一個機會在後臺線程上的多個小提取之間進行處理。

另一種方法是創建一個單獨的持久存儲協調器,將您的後臺MOC連接到該存儲並執行您的抓取。如果您使用多個持久性存儲協調器,則可以同時擁有多個讀取器,並且它們不會永久阻塞對方。

編輯

我肯定想過打破它。但這不是 的情況,我試圖讓兩個同時取出很好地012.一起玩。 MT在這一點上不應該接觸Core Data。 - BrianJStafford

您是否使用UIManagedDocument,或已創建的主線程在MOC,或NSMainQueueConcurrencyType

我問,因爲核心數據確保所有「用戶事件」每次通過運行循環處理。我從來沒有深入研究過如何完成這項工作,但我想他們會在主線程上爲MOC的運行循環添加一個處理程序。

核心數據死了很簡單,除非你有多個MOC。然後,交互非常複雜,以至於很難確定沒有實際代碼的情況。

如果可以,請發佈創建任何/所有MOC的代碼以及使用這些MOC的代碼。

我真誠地懷疑,如果與當前上下文無關,那麼他們會在運行循環處理中實際獲得對商店的鎖定,因此可能會與後臺MOC進行一些隱藏的交互。我敢打賭,你的應用程序中仍然存在一些你不認爲你正在做的事情,那就是與主要的MOC進行背景調查。

如果您願意,您可以輕鬆追蹤[_PFManagedObjectReferenceQueue _processReferenceQueue:]的調用方式。

測試1.創建一個簡單的項目,檢查Core Data框以獲取簡單的Core Data模板項目。請在_processReferenceQueue:處設置一個斷點,以確保您可以獲得該斷點。

測試2.註釋掉實例化核心數據內容的部分,並查看是否通過鏈接到框架來觸發斷點。

測試3.創建MOC,但不要做其他任何事情。只需創建它。重複看看你是否達到了斷點。

測試4.擺脫「默認MOC」的,所以你基本上是喜歡Test 2.現在,創建一個私有隊列MOC,通過performBlock用它做一個簡單的交易,看看你是否命中斷點。

你可以看到這是怎麼回事。基本上,確定非常簡單的用例組合導致您在主線程中打斷點

至少會讓你知道框架本身是否會導致這種情況發生,這反過來會讓你知道你的複雜應用正在做什麼。

編輯

好奇心得到了我的好。我只是跑了幾個簡單的測試,在兩個設置斷點:

-[NSManagedObjectContext(_NSInternalAdditions) lockObjectStore] 
-[_PFManagedObjectReferenceQueue _processReferenceQueue:] 

我看不出來,除非我實際上搞亂了MOC。每個MOC都是限制併發類型。我創建了一個擁有100,000個簡單實體的數據庫

主線程使用FRC加載數據。我添加了一個按鈕,當按下該按鈕時,會啓動一個dispatch_async並獲取所有100,000個對象。

我在補充線程中看到兩個斷點,但在主線程中都沒有命中。如果我在主線程上獲取或更新MOC,當然,我會打到這些斷點。

我在模擬器上運行。

所以,我的結論是,CoreData本身並不負責你所看到的行爲。

可能是由於MOC上的某些配置,持久性商店協調員,持久存儲或您的上下文之間的某些未知交互。

沒有關於對象和實際代碼配置的進一步信息,我最好的結論是你在代碼中做了「某些事情」,導致這種情況發生。

您應該設置這些斷點,並查看您的代碼在命中時發生了什麼。

+0

我絕對想過要分手。但是這並不是我想讓兩個同時抓取很好地結合在一起的情況。 MT在這一點上不應該接觸Core Data。 – BrianJStafford

+0

您是否使用UIManagedDocument?您是否在主線程或NSMainQueueConcurrencyType上創建了MOC? –

+0

我沒有使用UIManagedDocument。 MT上有一個默認併發類型(NSConfinementConcurrencyType)的MOC。後臺線程有其自己的MOC,也有默認的併發類型。 – BrianJStafford