NSMutableArray collection and @Synchronized blocks
Asked Answered
B

2

6

In Objective C I'm using a NSMutableArray instance from various thread and I'm using @synchronized to make it thread safe. currently all my acces to this array are protected with a @synchronized block, even objectAtIndex: method. Nevertheless I wonder which methods call really need to be protected with @synchronized. do I need to protect read access ?

What would happens if 'ObjectAtIndex' is not protected and called at the same time that 'removeObject' ?

If all methods are protected by a @synchronized what about performance? (I'writing a tcp/udp game server and really don't want to overprotect these array if it would decrease perf or generate locks).

for example I suppose that the 'containsObject:' method will enumerate to find the object and that I should avoid a concurent call to 'removeObject:' in another thread.

Perhaps a good solution would be to have too different locks (for read and write access)...

Help and suggestion are welcome !

Thanks a lot.

Please find a sample code below to illustrate :

@interface TestClass : NSObject
{
    NSMutableArray * array;
}

@end

@implementation TestClass

- (id)init
{
    self = [super init];
    if (self)
    {
        array = [NSMutableArray array];
    }
    return self;
}

-(id)objectAtIndex:(NSUInteger)index
{
    @synchronized(array) **// IS IT USEFUL OR NOT ??**
    {
        return [array objectAtIndex:index];
    }
}

-(void)removeObject:(id)object
{
    @synchronized(array)
    {
        [array removeObject:object];
    }
}

-(void)compute
{
    @synchronized(array)
    {
        for (id object in array)
        {
            [object compute];
        }
    }
}

@end
Bitterling answered 13/6, 2013 at 15:44 Comment(0)
T
5

Yes, you have to synchronize read accesses to prevent them from happening simultaneously with mutations. Read accesses can safely run simultaneously with other read accesses, though.

If you have multiple readers, then it's worth investigating a read-write locking scheme. You can use pthread read-write locks (i.e. pthread_rwlock_...()).

Alternatively, you can use GCD on OS X 10.7+ and iOS 5+ with the "barrier" routines. Create a private concurrent queue. Submit all read operations to it in the normal fashion (e.g. dispatch_sync()). Submit mutation operations to it using a barrier routine such as dispatch_barrier_async(). (It can be asynchronous because you generally don't need to know that the mutation has completed before continuing. You only need to know that all reads submitted after you submit the mutation will see the results of the mutation, and the barrier guarantees that.)

You can learn more about this if you have access to the WWDC 2011 session video for Session 210 - Mastering Grand Central Dispatch.

Talanta answered 15/6, 2013 at 1:29 Comment(2)
I really like the GCD solution. As I'm using many mutable array (and dictionary too) that I would like to protect I wonder if I could create my own GCDMutableArray and GCDMutableDictionary that would override all the methods and add a dispatch_queue_t for each instance of array or dictionary. But is it a good solution to have 10 mutable dictionary with their own dispatch_queue ? And to create a different queue in each instance? dispatch_queue_create take an name as arg, how to generate this name? Perhaps this kind of protected array and dict already have been writen... but can't find anything.Bitterling
I don't think such classes are generally useful. It's almost never the case that one can achieve thread safety by concentrating on low-level classes such as containers. You almost certainly need to be analyzing you classes at the level of their abstraction to design their interfaces, not just their implementations, for thread safety. That said, dispatch queues are cheap and Apple encourages you to create as many as a sensible design calls for.Talanta
C
0

You need to be aware that what you are doing isn't actually helping very much. For example, if your array has ten elements and you call [myObject objectAtIndex:9] while another thread calls [myObject removeObject:someObject], then chances are that the first call accesses an array element that doesn't exist anymore and throws an exception.

objectAtIndex isn't really useful if other threads can remove or insert objects.

Consanguineous answered 18/2, 2014 at 23:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.