问题描述
总结:我在多线程场景中调用 [myMOC mergeChangesFromContextDidSaveNotification:notification]
时挂起我的应用程序。
具体情况如下:
我的应用程序从服务器下载一大堆数据,并在第一次启动时将其存储在Core Data 。它有几个部分。解析整个事情需要几秒钟,大部分时间花在两个块上。所以为了加快速度,我已经并行化这两个块。这是它的样子:
NSArray * arr = [self parseJsonData:downloadedNSData]; //将NSData转换为JSON数组
//使用NSJSONSerialization
NSMutableArray __block * first = [[NSMutableArray alloc] init];
NSMutableArray __block * second = [[NSMutableArray alloc] init];
//用一个循环将半部分的arr放入循环中
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_t group = dipatch_group_create();
dispatch_group_async(group,queue,^
{
for(id element in first)
{
[MyDataClass parseData:element]; //创建NSManagedObject子类,save
}
[self saveContext];
});
dispatch_group_async(group,queue,^
{
for(id元素in second)
{
[MyDataClass parseData:element]; // create NSManagedObject子类,保存
}
[self saveContext];
});
dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
[self saveContext]
$ c> save 。这当然是在多个线程上完成的,所以我需要为每个线程创建一个单独的 ManagedObjectContext
。这是通过在该对象( self
)上保留一个 NSMutableDictionary
调用描述
在 NSThread
)到 NSManagedObjectContext
当访问时,如果没有用于 [NSThread currentThread]
的MOC,它将创建一个新的,将其添加到字典并存储它。当创建每个MOC时,我订阅其更改通知:
NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(mergeChanges :)
name:NSManagedObjectContextDidSaveNotification object:createdMOC];
在 mergeChanges
的上下文,并调用 mergeChangesFromContextDidSaveNotification
在所有这些,除了那个线程发生这种情况。更具体地说,我使用 [performSelector:onThread:withObject:waitUntilDone:]
让每个MOC在创建的线程上执行此操作。
我也使用 [myMOC save]
和我的<$>锁定 NSLock
c $ c> mergeChanges 方法。如果我没有锁定那些,我有可可错误133020,其中意味着错误合并更改。 / p>
所以,这是我的日志记录告诉我发生了:
- 获取要保存的锁并开始保存
- 线程1上有合并上下文通知。线程1获取合并更改的锁并开始该过程。
- 线程2开始保存,但永远不会获取锁定以进行保存,因为它会为主线程合并MOC的更改,然后在执行后台线程的MOC时挂起。
那么,为什么在合并更改时会挂起?
我尝试过使用 [persistentStoreCoordinator lock] code> $ 周围的 [MOC save]
调用,而不是只有一个锁 NSLock
我还尝试在 [self saveContext]
之前添加 [NSThread sleepForTimeInterval:2]
更新2:也许更好的问题这里是为什么我得到合并冲突(可可错误133020)。这是预期的吗?
更新3:我已经发布了,以解决我如何做多线程的更大的上下文。
解决方案我发布了记录我的情况一点更好。这个当前的问题我认为范围有点狭窄。
In summary: My app is hanging when I call [myMOC mergeChangesFromContextDidSaveNotification:notification]
in a multithreaded scenario.
The detailed situation is this:
My app downloads a whole bunch of data from a server and stores it in Core Data on first launch. It comes in several parts. Parsing the whole thing takes several seconds, and most of that time is spent on two of the chunks. So in order to speed it up, I've parallelized those two chunks. This is what it looks like:
NSArray *arr = [self parseJsonData:downloadedNSData]; //turns NSData into JSON array
//using NSJSONSerialization
NSMutableArray __block *first = [[NSMutableArray alloc]init];
NSMutableArray __block *second = [[NSMutableArray alloc]init];
//put half of arr in first and half in second with a loop
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dipatch_group_create();
dispatch_group_async(group, queue, ^
{
for (id element in first)
{
[MyDataClass parseData:element]; //create NSManagedObject subclass, save
}
[self saveContext];
});
dispatch_group_async(group, queue, ^
{
for (id element in second)
{
[MyDataClass parseData:element]; //create NSManagedObject subclass, save
}
[self saveContext];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
[self saveContext]
calls save
on the MOC. This is of course done on multiple threads, so I need to create a separate ManagedObjectContext
for each thread. This is accomplished by exposing the MOC via a property on this object (self
) that maintains an NSMutableDictionary
of thread names (call description
on NSThread
) to NSManagedObjectContext
s. When accessed, if there isn't a MOC for [NSThread currentThread]
, it creates a new one, adds it to the dictionary, and stores it. When each MOC is created, I subscribe to its change notifications:
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification object:createdMOC];
In mergeChanges
, I loop through my dictionary of contexts and call mergeChangesFromContextDidSaveNotification
on all of them except the one for the thread that this is happening on. More specifically, I use [performSelector:onThread:withObject:waitUntilDone:]
to have each MOC do this on the thread it's created for.
I'm also locking using NSLock
around both the [myMOC save]
and my mergeChanges
method. If I didn't lock around those, I got "Cocoa error 133020", which apparently means an error merging changes.
So, this is what my logging tells me is happening:
- Thread 1 acquires the lock to save and begins saving
- A merge context notification comes on thread 1. Thread 1 acquires the lock for merging changes and begins that process. It merges changes for the MOC for the main thread and then hangs when doing one of the MOCs for the background threads.
- Thread 2 starts to save but never acquires the lock for saving, because the other thread is stuck trying to merge changes.
So, why is it hanging when merging changes? Is there a better way to handle this scenario?
Update: I have tried using [persistentStoreCoordinator lock]
around my [MOC save]
call instead of just a lock with NSLock
, to no avail. I also tried adding [NSThread sleepForTimeInterval:2]
before the call to [self saveContext]
in one of the dispatch_group_async
calls, and it didn't help.
Update 2: Perhaps the better question here is why I was getting merge conflicts (Cocoa Error 133020). Is that expected? Am I doing the merge right (merging to all contexts except the one saving)?
Update 3: I've posted another question to address the larger context of how I'm doing the multithreading.
解决方案 I've posted this question documenting my situation a little better. This current question I think is a little narrow in scope.
这篇关于挂起mergeChangesFromContextDidSaveNotification(和合并冲突)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!