【发布时间】:2016-07-25 08:52:59
【问题描述】:
我听说过很多关于 CoreData 和并发性的问题。因此,我决定使用虚拟代码尝试一些场景。我无法完全解释所有观察结果。任何指针将不胜感激。
案例 1 相同的托管对象在两个不同的地方不断变化,主线程和后台线程使用下面的代码。未执行托管对象内容保存。
观察:没有崩溃。我看到“numberOfSales”的值在“主线程”和“后台队列”中读取的值不同。它们最终变得相同、分歧、变得相同等等。所以,我猜这是“最终一致性”的表现。
这是预期的行为吗?即,从多个线程对同一托管对象上下文中的对象进行更改似乎没问题。
案例 2 这两段代码还执行将托管对象上下文保存到持久存储
观察:随机崩溃。这是否意味着真正的问题是当您尝试从多个线程将内容存储到持久存储时?
案例 3 我使用串行队列序列化获取请求。如下代码示例 2 所示。
观察:没有崩溃。但我期待获取请求是串行的:一个来自主线程,一个来自后台 Q。但我看到它们中只有一个执行。为什么会这样?
在后台 Q 执行的代码块
dispatch_async(backgroundQueue, ^(void) {
while (1) {
sleep(1);
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (Person *info in fetchedObjects) {
NSLog(@"BackQ Name: %@, SSN: %@, Num sales = %@", info.name,info.ssn, info.numberOfSales);
info.numberOfSales = @(2);
}
//In case 1: The save to persistent store part below is commented out, in case 2: this part exists
if (![self.managedObjectContext save:&error]) {
NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}
}
});
在主线程中执行的代码块
while (1) {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"(name = %@)",@"XXX"]] ;
NSError *error;
NSArray <Person *>*fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
fetchedObjects[0].numberOfSales = @([fetchedObjects[0].numberOfSales integerValue] + 1);
NSLog(@"Fore Name: %@, SSN: %@, Num sales = %@", fetchedObjects[0].name,fetchedObjects[0].ssn, fetchedObjects[0].numberOfSales);
//In case 1: The save to persistent store part below is commented out, in case 2: this part exists
if (![self.managedObjectContext save:&error]) {
NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}
}
代码示例 2
self.coreDataQ = dispatch_queue_create("com.smarthome.coredata.bgqueue2", DISPATCH_QUEUE_SERIAL);
代码示例 2:后台代码 Q
dispatch_async(backgroundQueue, ^(void) {
while (1) {
dispatch_async(self.coreDataQ, ^(void) {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSError *error;
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
for (Person *info in fetchedObjects) {
NSLog(@"BackQ Name: %@, SSN: %@, Num sales = %@", info.name,info.ssn, info.numberOfSales);
info.numberOfSales = @(2);
}
if (![self.managedObjectContext save:&error]) {
NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}
});
}
});
代码示例 2:主线程中的代码
while (1) {
dispatch_async(self.coreDataQ, ^(void) {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"(name = %@)",@"XXX"]] ;
NSError *error;
NSArray <Person *>*fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
fetchedObjects[0].numberOfSales = @([fetchedObjects[0].numberOfSales integerValue] + 1);
NSLog(@"Fore Name: %@, SSN: %@, Num sales = %@", fetchedObjects[0].name,fetchedObjects[0].ssn, fetchedObjects[0].numberOfSales);
if (![self.managedObjectContext save:&error]) {
NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}
});
}
【问题讨论】:
-
不能在后台线程中直接使用objectContext,如果要实现,必须创建具有后台线程能力的Child Context。
-
是的,这就是我在 CoreData 编程指南中看到的理论。但我在实践中没有看到这一点。实际上在代码示例 2 中,只有“后台队列块”中的 NSLOg 正在执行。
-
开启并发断言,如oleb.net/blog/2014/06/core-data-concurrency-debugging所示——实际上,通读全文
标签: objective-c multithreading core-data grand-central-dispatch