multithreading - How to prevent race conditions when merging changes across threads? -


a typical setup: have main thread mainmoc , background thread own backgroundmoc. background thread performs read-only operations on backgroundmoc dispatching blocks backgroundqueue.

the backgroundmoc needs merge changes mainmoc register nsmanagedobjectcontextdidsavenotification , like

- (void)mainmocdidsave:(nsnotification *)notification {     dispatch_async(backgroundqueue, ^{         [backgroundmoc mergechangesfromcontextdidsavenotification:notification];     }); } 

let's user deletes object in mainmoc. code above not seem safe me, since merge done @ point in future. until merge done, there might still blocks on backgroundqueue trying use deleted object.

the obvious solution use dispatch_sync instead (or performblockandwait, performselector:onthread:...) instead. code snippets see on interwebs, seems doing. i'm not comfortable solution either.

the name nsmanagedobjectcontextdidsavenotification implies save has happened when notification delivered. corresponding row has been deleted underlying database (assuming sqlite store). dispatch_sync have wait other blocks on queue finish before can merge changes, , these other blocks still try work deleted object, leading nsobjectinaccessibleexception.

it seems me correct way merge changes 1 thread/queue to

  1. subscribe nsmanagedobjectcontextwillsavenotification , nsmanagedobjectcontextdidsavenotification on background thread.
  2. on nsmanagedobjectcontextwillsavenotification: empty backgroundqueue , suspend operations dispatch new blocks queue.
  3. on nsmanagedobjectcontextdidsavenotification: merge changes synchronously.
  4. resume normal operation on background queue.

is correct approach or missing something?

i use following structure in 2 projects in experienced similar troubles did. first of use singleton service ensure there 1 background thread merging , reading changes.

appdelegate.m

- (nsmanagedobjectcontext *)managedobjectcontext {     if (_managedobjectcontext != nil) {         return _managedobjectcontext;     }      nspersistentstorecoordinator *coordinator = [self persistentstorecoordinator];     if (coordinator != nil) {         // crucial use 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 implementation code handle error appropriately.              // abort() causes application generate crash log , terminate. should not use function in shipping application, although may 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 use 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]; } 

Comments

Popular posts from this blog

get url and add instance to a model with prefilled foreign key :django admin -

css - Make div keyboard-scrollable in jQuery Mobile? -

android - Keyboard hides my half of edit-text and button below it even in scroll view -