magiclantern
magiclantern

Reputation: 798

NSMutableArray Thread Concurrency with GCD

I have an NSMutableArray in a "sharedStore"-pattern singleton.

Publicly, it's accessible only through methods that cast it as an NSArray. Within the class, it's

@property (nonatomic, copy) NSMutableArray *myItems;

This array never gets manipulated outsdie the singleton but ViewControllers send the singleton messages to manipulate this controller. Some of these messages empty the array, some re-populate it, etc.

Having ended up in a situation where the array was empty in one method call and not yet empty in the next, I've started implementing some concurrency behaviour.

Here's what I'm doing so far:

In the .m file of the singleton, I have a

@property (nonatomic, strong) dispatch_queue_t arrayAccessQueue;

In my singleton's initializer it gets created as a serial queue. And then, every method that has anything to do with mutating this array does so from within a dispatch_sync call, for example:

dispatch_sync(self.arrayAccessQueue, ^{
    [_myItems removeAllObjects];
});

This has made things better and has made my app behave more smoothly. However, I have no way of quantifying that beyond it having fixed that one odd behaviour described above. I also kind of feel like I'm in the dark as to any problems that may be lurking beneath the surface.

This pattern makes sense to me, but should I be using something else, like @synchronize or NSLock or NSOperationQueue? Will this come back to bite me?

Upvotes: 4

Views: 787

Answers (3)

Andrea
Andrea

Reputation: 26385

Using a GCD queue concurrent and providing sort of accessor to your array you can synchronize reading and writing by using dispatch_sync while reading and dispatch_barrier_async while writing.

- (id)methodToRead {
  id __block obj = nil;
  dispatch_sync(syncQueue, ^{
     obj = <#read_Something#>;
  });
  return obj;
}

- (void) methodsForWriting:(id)obj {
   dispatch_barrier_async(syncQueue, ^{
    // write passing obj to something
  });
}

This will guarantee that during writing everything is locked from reading.

Upvotes: 1

rmaddy
rmaddy

Reputation: 318824

Using dispatch_sync is fine as long as you wrap all array reads and writes and you ensure it is a serial queue.

But you could improve things by allowing concurrent reads. To do this, use dispatch_sync around all array reads and use dispatch_barrier_sync around all array writes. And setup the queue to be concurrent.

Do this ensures only a single write can happen at a time, reads will be block until the write is done, and a write will wait until all current reads are done.

Upvotes: 4

Holly
Holly

Reputation: 5300

Using GCD is the right choice. The only "gotcha" is that you need to do ALL operations on that queue: add, remove, insert, etc.

I will also mention you need to ensure that you do not use a concurrent queue. You should be using a serial queue, which is the default anyways.

Upvotes: 0

Related Questions