Question

I had issues with thread safety. I have a queue that when I modified the contents caused errors across threads. I hadn't used locks before, but thought I try. I added a lock around all code that manipulated the backing NSMutableArray for my queue. The issue, I think, is that I didn't use the same lock for all of them. I created a new instance of NSLock in each method that modified the array. I'm assuming I should use one NSLock ivar to protect the array. But my confusion comes from the fact that it worked once I added it. Below is a sample. I assume that everywhere that I created a new NSLock, I should have just used one ivar NSLock. I would think that this code just locked enqueues against other enqueues and the dequeues against other dequeues and not the enqueues against the dequeues. Clarification would be great.

@implmentation

...    

- (void)enqueue:(id)obj
{
  NSLock *arrayLock = [[NSLock alloc] init];
  [arrayLock lock];
   [_backingStore addObject:obj];
  [arrayLock unlock];
}

- (id)dequeue
{
  NSLock *arrayLock = [[NSLock alloc] init];
  [arrayLock lock];
  id result = [_backingStore firstObject];

  if( result )
  {
    [_backingStore removeObjectAtIndex:0];
  }

  [arrayLock unlock];
  return result;
}

...

@end
Was it helpful?

Solution

Yes, you do need to use the same NSLock instance to lock both accesses to the array. As with so many multithreading errors, the problem may have seemed to disappear due to timing differences caused by adding the additional code. Or, it could just be that you got (un)lucky and the problem didn't appear when you tested the second time.

For whatever it's worth, NSLock is just one way of locking access to critical sections in Objective-C. You can also use @synchronized() which may be easier from a code-complexity point of view:

@synchronized(someSharedToken) {
    // Do your array access here
}

You could also use a serial dispatch queue to serialize access to the resource. That has several advantages, not least of which is the ability to dispatch work to it without waiting for that work to finish on the current thread. It's also less expensive to dispatch work to a queue than it is to take out a lock. See the Creating Serial Dispatch Queues section of Apple's Concurrency Programming Guide.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top