gpt4 book ai didi

multithreading - 跨线程合并更改时如何防止竞争状况?

转载 作者:行者123 更新时间:2023-12-03 13:13:07 25 4
gpt4 key购买 nike

一个典型的设置:我们有一个带有mainMOC的主线程和一个带有自己的backgroundMOC的后台线程。后台线程通过将块分配给backgroundMOC来对backgroundQueue执行只读操作。
backgroundMOC需要合并mainMOC中的更改,因此我们注册NSManagedObjectContextDidSaveNotification,然后执行类似的操作

- (void)mainMocDidSave:(NSNotification *)notification {
dispatch_async(backgroundQueue, ^{
[backgroundMoc mergeChangesFromContextDidSaveNotification:notification];
});
}

假设用户删除了 mainMOC中的对象。上面的代码对我来说并不安全,因为合并将在将来的某个时候完成。在合并完成之前, backgroundQueue上可能仍存在尝试使用已删除对象的块。

显而易见的解决方案是改为使用 dispatch_sync(或 performBlockAndWaitperformSelector:OnThread:...)。从我在互联网上看到的代码片段来看,这似乎是每个人都在做的事情。但是我也不满意这种解决方案。

名称 NSManagedObjectContextDidSaveNotification表示在传递通知时已进行保存。因此,相应的行已从基础数据库中删除(假设为sqlite存储)。 dispatch_sync将必须等待队列中的其他块完成才能合并更改,并且这些其他块仍可以尝试使用已删除的对象,从而生成 NSObjectInaccessibleException

在我看来,将更改从一个线程/队列合并到另一个线程/队列的正确方法是
  • 在后台线程上订阅NSManagedObjectContextWillSaveNotificationNSManagedObjectContextDidSaveNotification
  • NSManagedObjectContextWillSaveNotification上:清空backgroundQueue并暂停将新块分发到队列的所有操作。
  • NSManagedObjectContextDidSaveNotification上:同步合并更改。
  • 在后台队列上恢复正常操作。

  • 这是正确的方法还是我错过了什么?

    最佳答案

    我在两个项目中使用了以下结构,在这些项目中,您遇到了类似的麻烦。首先,我使用单例服务来确保只有一个后台线程合并和读取更改。

    AppDelegate.m

    - (NSManagedObjectContext *)managedObjectContext {
    if (_managedObjectContext != nil) {
    return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
    // It is crucial to use the correct concurrency type!
    _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
    }

    - (void)saveContext {
    NSError *error = nil;
    NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
    if (managedObjectContext != nil) {
    if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
    // Replace this implementation with code to handle the error appropriately.
    // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    abort();
    }
    else {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"ParentContextDidSaveNotification" object:nil];
    }
    }
    }

    BackgroundService.m
    - (id)init {
    self = [super init];

    if (self) {
    [self managedObjectContext];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(parentContextDidSave) name:@"ParentContextDidSaveNotification" object:nil];
    }

    return self;
    }

    - (NSManagedObjectContext *)managedObjectContext {
    if (!_managedObjectContext) {
    // Again, make sure you use the correct concurrency type!
    _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [_managedObjectContext setParentContext:[(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]];
    }

    return _managedObjectContext;
    }


    - (BOOL)saveContext {
    @synchronized(self) {
    BOOL successful = YES;

    // Bad practice, process errors appropriately.
    [[self managedObjectContext] save:nil];

    [[[self managedObjectContext] parentContext] performBlock:^{
    [(AppDelegate *)[[UIApplication sharedApplication] delegate] saveContext];
    }];

    return successful;
    }
    }

    - (void)parentContextDidSave {
    [[self managedObjectContext] reset];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"ManagedObjectContextResetNotification" object:nil];
    }

    关于multithreading - 跨线程合并更改时如何防止竞争状况?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15381896/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com