与核心数据的并发性
Posted
技术标签:
【中文标题】与核心数据的并发性【英文标题】:Concurrency with core data 【发布时间】:2012-08-18 10:42:39 【问题描述】:我正在使用多线程来获取数据、解析数据、创建对象并存储它们。完成这一切后,我希望显示窗口。 但现在我有两个问题:
我遇到了僵局 我的屏障不能作为屏障。我认为死锁是因为我同时在多个线程中更新 managedObjectContext。
所以我用 ConcurrencyType 更改了我的 managedObjectContext:
__managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
并为并发队列创建了一个importContext并分配了parentContext:
NSManagedObjectContext *importContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
importContext.parentContext = self.managedObjectContext;
并将我的操作放在 importContext 的 performBlock 中:
[importContext performBlock:^
dispatch_async(backgroundQueue, ^
[myObject methodAWithContext:importContext];
);
dispatch_async(backgroundQueue, ^
[myObject methodBWithContext:importContext];
);
dispatch_async(backgroundQueue, ^
[myObject methodCWithContext:importContext];
);
dispatch_barrier_async(backgroundQueueM, ^
// create barrier to wait for the first 3 threads to be completed.
dispatch_async(dispatch_get_main_queue(), ^
// Save the data from the importContext tot the main context on the main queue
NSError *importError = nil;
[importContext save:&importError];
[importContext.parentContext performBlock:^
NSError *parentError = nil;
[importContext.parentContext save:&parentError];
];
[self.window makeKeyAndVisible];
);
);
];
方法一: 在每种方法中,我选择对象的一个子集,删除它们,然后创建新对象并保存它。 (我认为删除比获取并检查要创建的每个对象的存在更快)。 所以: 在方法 A 中,我选择所有 AObject,删除它们并创建新的 AObject。 在方法 B 中,我选择所有 BObject,删除它们并创建新的 BObject。 在方法 C 中,我选择所有 CObject,删除它们并创建新的 CObject。
但随后我收到错误消息“NSManagedObjectContext 无法删除其他上下文中的对象”。
所以方法 2: 我删除了删除。但现在我得到了各种不同的错误...... 并且屏障不会等待其他线程被执行。
Q1:我做错了什么?
Q2:如何获得等待 3 个线程完成的屏障
Q3:如何删除/清除各个线程上的对象?
(我已经阅读了 Apple 的发行说明和文档,但我找不到关于多线程和 managedContext 组合的清晰解释。)
【问题讨论】:
【参考方案1】:您不能在performBlock
内调用dispatch_async
。 NSPrivateQueueConcurrencyType
类型的托管对象上下文有它自己的调度队列来执行操作。
您尝试通过将多个操作移动到不同的调度队列来并行执行多个操作,但这是不可能的。
如果确实要并行执行多个操作,则必须为每个操作创建一个私有并发类型 MOC。
添加:
有几种方法可以等待所有操作完成:
您可以在每个performBlock:
末尾增加一个计数器,并检查其值是否(在您的示例中)为 3。
您可以为每个初始值为零的操作创建一个信号量 (dispatch_semaphore_create
),等待所有信号量 (dispatch_semaphore_wait
) 并在每个 performBlock
的末尾发出信号量。
而且我确信有更好/更优雅/更复杂的方法来做到这一点。
但是:当我重新阅读您的问题时,我发现您试图推迟
[self.window makeKeyAndVisible];
直到所有核心数据获取操作都完成。这不是一个好的设计,因为在您的数据导入完成之前用户什么都看不到。
更好的设计是立即显示初始视图,并在后台操作获取数据时更新该视图。
【讨论】:
答案是接近我建议的只是添加评论。您需要做的是选择两种解决方案之一。您使用传统方式 - 您创建一个串行调度队列,在其中创建并执行所有 MOC 工作((NSConfinementConcurrencyType) - 或者您要求 MOC 为您创建一个队列(NSPrivateQueueConcurrencyType),然后仅使用 performBlock 与该 MOC 交互. 在任何一种情况下,您都可以在其他调度块中使用 MOC 进行操作,如果且仅当在第一种情况下您调度到您的私人队列或第二次您使用 usr [moc performBlock:...]。 谢谢!现在,并发与 3 个单独的 MOC 一起工作,并且没有 dispatch_async。但是现在,我仍然需要知道如何在显示我的窗口之前等待 3 个块完成。我怎样才能做到这一点? 我知道,我正在考虑一个更好的设计(但是:它是非常小的数据,所以不需要很长时间。但是在串行中,它需要几秒钟的时间。但是对于最重要的部分,我有一个显示的 networkActivityIndicator,我希望它在所有数据获取和对象创建完成后停止。 另外:在我的第一个线程设计中,视图是显示的,并且在获取/对象创建之后显示 navigationController 的子视图。但现在,它不再是了。立即显示子视图。没问题,但我想让用户知道还有一些活动。 @RobertvdBerg:我试图解释您最初设计中的错误并展示一些替代方案。您已确认并发现在可以工作,因此您可以考虑“接受”答案。以上是关于与核心数据的并发性的主要内容,如果未能解决你的问题,请参考以下文章