synchronized blocks and dispatch_async
Asked Answered
S

2

13

What happens to the lock in IOS using @synchronized() when we call dispatch_async() within the block.

For ex:

    id myID
-(void) foobar
{
    @synchronized(myID){
        dispatch_async(){ //do stuff with myID};
    }
}

Is the lock still valid within the dispatch_async call? Or more importantly is there any drawbacks in using another @synchronized() call inside dispatch_async()?

Saar answered 4/9, 2013 at 22:54 Comment(2)
What are you trying to achieve?Adar
BTW, am I correct in assuming that you are not reinstantiating myID at any point? The @synchronized block is unique to that particular instance of the object pointed to by myID, not the variable in general.Moya
M
10

Assuming you're trying to synchronize the interaction with this myID object in the background queue, you want it the other way around, the lock inside the dispatched block. Right now you have:

@synchronized(myID) {
    dispatch_async(queue, ^{
         // do stuff with myID
    });
}

That's synchronizing the process of adding the dispatched block to your queue, but does not synchronize what you're doing in the background. I suspect that's not what you meant.

You probably intended:

dispatch_async(queue, ^{
    @synchronized(myID) {
         // do stuff with myID
    }
});

It looks very similar, but results in an entirely different behavior. Now, the work dispatched to the background queue is being synchronized.

As a further refinement, if this dispatched block is possibly slow (and I assume it may be), then you'd probably want to constrain the @synchronized block as much as possible:

dispatch_async(queue, ^{

    // do slow stuff in preparation for interacting with `myID`

    @synchronized(myID) {
         // quickly do stuff with myID
    }

    // do anything else here
});

If you do all of the background block within a @synchronized block, you may defeat the entire purpose for dispatching it to the background, namely to minimize impact on the main queue. This last rendition mitigates that problem.

As a final observation, if you have a serial queue (or a non-global concurrent queue in which you do updates with a barrier), that's often used as a technique that eliminates the need for locks altogether, as long as all updates and inquiries for myID are dispatched to that queue. See Eliminating Lock-Based Code in the Concurrency Programming Guide.

Moya answered 5/9, 2013 at 1:37 Comment(2)
When you say "minimize impact on the main queue" you mean only that the background process is going to take longer, right? The work is still being done in the background queue and would not e.g. block the UI.Frigging
@Ixx - No, my point is that if (a) you do something time-consuming inside a @synchronized block on a background thread; and (b) if the main thread also needs to access myID (and therefore would have to use @synchronized, too), your background @synchronized block can easily block the main thread's, defeating the purpose of dispatching this stuff to a background queue altogether. So, when the background thread has to do @synchronized, make sure you get in and out as quickly as possible, keeping time-consuming stuff outside of the @synchronized block, if you can.Moya
M
4

The lock there would just prevent two different blocks being dispatched at once. However they're dispatched asynchronously, so they may be performed then or may be performed arbitrarily far in the future. The dispatch call also won't wait for them to complete.

So the stuff inside the block isn't synchronised. Options to achieve that with minimal changes are a synchronous dispatch or just @synchronizing within the block.

Depending on what you're doing, the best idea might be to establish a serial dispatch queue and dispatch your blocks onto that.

Matherne answered 4/9, 2013 at 22:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.