我聽到很多關於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,如果您想實現這一點,您必須創建具有後臺線程能力的子上下文。 – ogres
是的,這是我在CoreData編程指南中看到的理論。但我在實踐中沒有看到這一點。事實上,在代碼示例2中,只有「後臺隊列塊」中的NSLOg正在執行。 –
打開併發斷言,如http://oleb.net/blog/2014/06/core-data-concurrency-debugging/中所示 - 實際上,讀取整個事件 – jrturton