Ios 保存并发类型为NSPrivateQueueConcurrencyType的上下文时发生死锁

Ios 保存并发类型为NSPrivateQueueConcurrencyType的上下文时发生死锁,ios,core-data,Ios,Core Data,两天以来,我一直试图让核心数据与多个线程一起工作。我尝试了标准的线程限制方法,包括NSO操作、合并通知、使用objectWithId、每个线程的上下文字典,但仍然会遇到奇怪的死锁、不一致异常和其他一些糟糕的情况。它快把我逼疯了。。。此外,当两个线程都可能对共享持久存储进行更改时,我找不到一个关于如何在两个线程中管理上下文的示例或解释 我尝试使用新的iOS5方法,这本应该更容易,但我还是会出错。第一个问题是保存上下文时出现死锁。我删除了所有不必要的代码,但在以足够快的速度执行此代码时仍然会出现死锁

两天以来,我一直试图让核心数据与多个线程一起工作。我尝试了标准的线程限制方法,包括NSO操作、合并通知、使用objectWithId、每个线程的上下文字典,但仍然会遇到奇怪的死锁、不一致异常和其他一些糟糕的情况。它快把我逼疯了。。。此外,当两个线程都可能对共享持久存储进行更改时,我找不到一个关于如何在两个线程中管理上下文的示例或解释

我尝试使用新的iOS5方法,这本应该更容易,但我还是会出错。第一个问题是保存上下文时出现死锁。我删除了所有不必要的代码,但在以足够快的速度执行此代码时仍然会出现死锁(通过快速点击按钮):

NSManagedObjectContext*context=[StoreDataRetriever sharedRetriever].managedObjectContext;
对于(int i=0;i<5;i++){
n错误*错误=nil;
NSLog(@“主线程:%@,是主线程?%d”,[NSThread currentThread],[NSThread isMainThread]);
BOOL saveOK=[上下文保存:&错误];
如果(!saveOK){
NSLog(@“ERROR!!!正在主目录中保存上下文”);
}
[上下文执行锁:^{
NSLog(@“块线程:%@,[NSThread currentThread]);
n错误*错误=nil;
BOOL savedOK=否;
savedOK=[上下文保存:&错误];
如果(!savedOK){
NSLog(@“错误!!!正在块中保存上下文”);
}
}];
}
数据库没有其他更改,没有任何更改,只有保存上下文。这个代码有什么问题?它应该是什么样子


注意:[StoreDataRetriever sharedRetriever].managedObjectContext是在appDelegate中使用initWithConcurrencyType:NSPrivateQueueConcurrencyType创建的。

该代码是怎么回事?在线程上同步保存上下文,然后在上下文专用队列上安排保存。5次。因此,基本上,您可能有两个save操作,一个同步,一个异步,相互冲突

这显然是一个问题。您不应该使用队列外部的专用队列保存上下文。如果上下文队列上没有调度块,它将与当前上下文实现一起工作。但这是错误的

…
for (int i = 0; i < 5; i++) {            
    NSLog(@"Main thread: %@, is main? %d", [NSThread currentThread], [NSThread isMainThread]);
    __block NSError *error = nil;
    __block BOOL saveOK = YES;
[context performBlockAndWait: ^{
    saveOK = [context save: &error];
}];

    if (!saveOK) {
        NSLog(@"ERROR!!!");
    }
    …
…
对于(int i=0;i<5;i++){
NSLog(@“主线程:%@,是主线程?%d”,[NSThread currentThread],[NSThread isMainThread]);
__块N错误*错误=nil;
__block BOOL saveOK=是;
[上下文执行锁定和等待:^{
saveOK=[上下文保存:&错误];
}];
如果(!saveOK){
NSLog(@“ERROR!!!”);
}
…
有了这些代码,您可以同步地执行save操作,而且最肯定的是在同一个线程上执行save操作—这要感谢GCD—节省了上下文切换和同步功能,并且没有同时在该上下文上运行两个操作的风险

在使用NSMainQueueConcurrencyType时,同样的规则也适用,但有一个例外。该队列仅绑定到主线程和主线程。您可以从任何具有performBlock和performBlockAndWait的线程(如NSPrivateQueueConcurrencyType)和(例外:)的线程中使用主队列在上下文上调度块您可以直接在主线程上使用上下文

NSConfignementConcurrencyType将上下文绑定到特定线程,您不能使用GCD或块来处理此类上下文,只能使用绑定的线程。从今天起,没有什么理由使用该并发模型。如果必须使用,请使用它,但如果不一定要使用,请不要使用


编辑


这是一篇关于多Contexts设置的非常好的文章:

因此它对我的实际问题没有多大帮助:我必须在后台对NSManagedObjects进行耗时的计算,同时保持UI响应并执行一些操作(创建新的托管对象、更改数量等以响应用户点击)在主线程上。我仍然需要使用限制或可能创建子上下文?呃…子上下文很便宜。如果您的计算量很大,子上下文的成本几乎是零。因此,是的,这是一个很好的方法,使用performBlock在您为此目的创建的特定子上下文上安排您的计算,并保存chaNGE要么在结尾,要么在有意义的时候。这里有一篇关于这个主题的文章:不幸的是,我正在修改的现有代码是使用StoreDataRetriever singleton对象模式编写的,对这个对象的调用深深埋藏在代码中。在这个阶段很难更改它,所以我想我会使用旧的限制方法。我只是我必须找出我的代码出了什么问题……你有没有发现这个问题?我在使用线程限制时遇到了几乎相同的问题。好了,我做的每件事都是按部就班的,但仍然会出现死锁(甚至并发读取)。
…
for (int i = 0; i < 5; i++) {            
    NSLog(@"Main thread: %@, is main? %d", [NSThread currentThread], [NSThread isMainThread]);
    __block NSError *error = nil;
    __block BOOL saveOK = YES;
[context performBlockAndWait: ^{
    saveOK = [context save: &error];
}];

    if (!saveOK) {
        NSLog(@"ERROR!!!");
    }
    …