Hot Licks
Hot Licks

Reputation: 47739

Is there a "fair" synchronization mechanism for iOS

I have an app that tends to do a lot of DB activity in a background thread (downloading large database updates), and quite often this process "starves" the UI thread -- I know that the DB is being released at intervals, but the @synchronized mechanism that is used is not "fair" and allows the background thread to immediately reacquire the lock.

Is there another mechanism that is reasonably efficient and well-supported (and not too hard to retrofit) (and "fair") that could be used instead?

Upvotes: 1

Views: 418

Answers (3)

Richard J. Ross III
Richard J. Ross III

Reputation: 55583

I cannot reproduce your issue. Here I have a simple program (albeit running on mac), based on @synchronized and pthreads, which clearly shows that @synchronized works as expected, assuming you release your locks:

void *backgroundThread(void *data)
{
    while (true)
    {
        @synchronized (globalMutex)
        {
            usleep(USEC_PER_SEC / 3);
        }
    }

    return NULL;
}

int main()
{
    pthread_t bgThread = NULL;
    globalMutex = [NSObject new];

    pthread_create(&bgThread, NULL, &backgroundThread, NULL);

    NSTimeInterval lastTime = [[NSDate date] timeIntervalSinceReferenceDate];
    while (true)
    {
        @synchronized (globalMutex)
        {
            NSTimeInterval elapsed = [[NSDate date] timeIntervalSinceReferenceDate] - lastTime;
            NSLog(@"Main Thread 'came up for air' after %lf seconds", elapsed);

            lastTime += elapsed;
        }
    }
}

Output:

TestProj[1494:303] Main Thread 'came up for air' after 0.000015 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.003136 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.000637 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.000610 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.000697 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.000576 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.000571 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.337343 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335533 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335253 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335309 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335367 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335223 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335754 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335271 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335211 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.334555 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335245 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335203 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335262 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335252 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335667 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335278 seconds
TestProj[1494:303] Main Thread 'came up for air' after 0.335309 seconds

So unless you aren't properly releasing your locks, @synchronized is exactly what you need in this scenario. Please elaborate on what your synchronization code actually looks like, so that we can help you more.

Upvotes: 2

Chris McGrath
Chris McGrath

Reputation: 1936

If you are worried about blocking your UI thread, you could create a serial dispatch queue for your database operations. This would enable you to queue up some DB operation from the UI thread, without blocking it (the block of work will just be copied to the heap, stored until its turn to be processed).

For example, from the UI thread, you could:

database_queue = dispatch_queue_create("com.example.queue", NULL);

dispatch_async(database_queue, ^{
    // Operations on the DB here
});
// The UI will continue responding immediately after here...

The queue will process each block one at a time, providing the synchronisation that you need - you just need to make sure all DB operations are done from that one queue.

If you do need to wait until the work has indeed been done, you can swap out the async call for a sync call in those particular instances:

dispatch_sync(database_queue, ^{
    ....
    ....

Upvotes: 0

Victor Ronin
Victor Ronin

Reputation: 23288

As I underestand @synchronized is a convenient way to create mutex locks. It doesn't have to be "fair".

I would recommend to read about GCD. It has a lot of interesting stuff to put all the work which needs to be done in queues (which should provide better resource sharing).

Upvotes: 1

Related Questions