0

I declared a atomic NSMutableArray to save objects

@property (atomic, strong) NSMutableArray *list;
@synthesize list;

Then I'm using a thread to save object like:

__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ // writing files into the disk in background
    // do something..

    [weakSelf.list addObject:object]; // add object at the end of the list

    if (!weakSelf.threadCreateFlag) {
        NSInvocationOperation *newoperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(createThreadMethod) object:nil]; // create thread to remove from the array

        [weakSelf.serailQueue addOperation:newoperation];
    }
});

And I'm trying to read the object at index 0 and remove it from the array

- (void)createThreadMethod {

self.threadCreateFlag = YES;

do {
    NSLog(@"loop");
    @autoreleasepool {

        NSDictionary *object = (NSDictionary *)[self.list objectAtIndex:0];
        [self.list removeObjectAtIndex:0];

        // doing something...
    }
} while(self.list.count > 0);

self.photoSavingLock = NO;
}

I am confronted with an issue - sometimes, the array contains object 0x0, and my app will crash. I did some research about it, I think because removeObjectAtIndex is not thread-safe. So I'm trying to add a global @property (atomic, strong) NSLock *lock around read/remove and add function. But this time I got [NSLock deadlock].

What is the proper way to make this scenario thread-safe?

Don Work
  • 45
  • 3
Xin
  • 47
  • 7
  • you should look at `@synchtonized` [here](http://stackoverflow.com/questions/15392726/does-synchronized-guarantees-for-thread-safety-or-not), [here](http://stackoverflow.com/questions/10163456/locked-up-waiting-for-synchronized/19429880#19429880) and [here](http://stackoverflow.com/questions/6317889/what-does-synchronized-do) – Lulylulu Apr 06 '16 at 15:35
  • Look at http://stackoverflow.com/questions/6234344/help-understanding-class-method-returning-singleton or http://stackoverflow.com/questions/4251087/singleton-or-class-methods – Patrick Apr 06 '16 at 15:37
  • I would advocate for a private concurrent queue to synchronise reads and writes to the array, by using `dispatch_barrier_async` to perform writes and `dispatch_sync` to perform reads. But `@synchronized` is a nice and convenient way of doing it too, although has a fraction extra overhead. See http://stackoverflow.com/questions/17599401/what-advantages-does-dispatch-sync-have-over-synchronized – Hamish Apr 06 '16 at 15:40
  • `@synchronized` is relic of the path as `atomic` property. No body uses them now. You should use serial queue to access yours list. – Cy-4AH Apr 06 '16 at 15:43

1 Answers1

0

The easiest two ways to make things thread safe is either by using @synchronized or by dispatching things to a serial queue.

My strategy for maximum efficiency: Have a mutable and an immutable array. When you want to modify the array:

- (void)modifyArray {
    @synchronized (self) {
        if (_mutableArray == nil) {
            _mutableArray = [(_immutableArray ?: @[]) mutableCopy];
            _immutableArray = nil;
        }
        // Modify the mutable array. 
    }
}

- (NSArray*)immutableArray {
    @synchronized (self) {
        if (_mutableArray != nil) {
            _immutableArray = [_mutableArray copy];
            _mutableArray = nil;
        }
        return _immutableArray;
    }
}

Obviously you would get the immutableArray once, have a call that erases the list, and then iterate through the immutableArray. Or have a method - (NSDictionary*)removeFirstElement which removes the first element and returns nil if there is nothing else to remove.

gnasher729
  • 51,477
  • 5
  • 75
  • 98