Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dispatch_sync always scheduling a block on Main Thread

I am executing a block using dispatch_sync and the block is executed correctly. But this block is executed on the main thread. As per the Apple Doc:

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue.

which means (or what I understood) that current process that is being executed will run on a separate thread.

Below is the code that I am using to judge what's going on. It is being called inside NSURLConnection's didReceiveData: delegate method (I know I should not do that inside the didReceiveData: delegate method - but this is just a sample to focus on dispatch_sync). Following are the different ways that I can assume as a proof of my conclusion:

  1. Using dispatch_sync on a Global Concurrent Queue

       dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
            if ([NSThread isMainThread]) {
                NSLog(@"Main Thread");
            }
            else
                NSLog(@"Not on Main Thread");
    
            //Some Process
        });
    

Output -

         Main Thread
         Main Thread 
         Main Thread
         // Main Thread printed till didReceiveData: gets called

  1. Using dispatch_sync on a self created queue using dispatch_queue_create

    // Create queue somewhere else like this
    dispatch_queue_t newQueue = dispatch_queue_create("WriteQueue", DISPATCH_QUEUE_SERIAL);
    
    
       dispatch_sync(newQueue, ^{
    
            if ([NSThread isMainThread]) {
                NSLog(@"Main Thread");
            }
            else
                NSLog(@"Not on Main Thread");
    
            //Some Process
        });
    

Output -

         Main Thread
         Main Thread 
         Main Thread
         // Main Thread printed till didReceiveData: gets called

I am a bit surprised here, block is executed always on the main thread or am I missing something. Because it seems to be going against the Apple Doc I think so. Does anyone know what this is all about?

Update: As per other discussions I understand that dispatch_sync executes a block on the same thread (most of the times), then why apple docs' statements are contradicting in some ways. Why apple says "The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue." Or am I still missing something?

like image 327
Evol Gate Avatar asked Dec 20 '12 12:12

Evol Gate


People also ask

What happens if you use async with dispatchqueue?

And potentially even deadlocking your app, which can happen if you call DispatchQueue.main.sync from code that is already on the main queue. Dispatching to the main queue with async does not carry this same risk, even if you're already on the main queue.

How do I schedule work in a dispatch queue?

All dispatch queues can schedule their work to be executed sync or async. Typically you will want to schedule work async because scheduling your work synchronously would halt the execution of the current thread, wait for the target thread execute the closure that you pass to sync, and then resume the current thread.

Which thread does this function execute blocks on?

As a performance optimization, this function executes blocks on the current thread whenever possible, with one exception: Blocks submitted to the main dispatch queue always run on the main thread.

How do I avoid blocking the current thread in a queue?

Remember to always dispatch your queue to the main queue asynchronously using DispatchQueue.main.async to avoid blocking the current thread. And potentially even deadlocking your app, which can happen if you call DispatchQueue.main.sync from code that is already on the main queue.


3 Answers

dispatch_sync() dispatches the block on the same thread, that's normal.

EDIT

Apple's Documentation does not only says this, also says this:

As an optimization, this function invokes the block on the current thread when possible.

As a side note (I know you're talking about the synchronous version, but let's precisate this) I would say that also dispatch_async() may cause multiple blocks to be executed in the same thread.

like image 54
Ramy Al Zuhouri Avatar answered Oct 12 '22 07:10

Ramy Al Zuhouri


For a background block, use

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // background code
});

Note it's _async and not _sync

EDIT: Likewise, to execute something on main thread, use

dispatch_async(dispatch_get_main_queue(), ^{
    // main thread code
});
like image 31
Ismael Avatar answered Oct 12 '22 07:10

Ismael


It is important to realize that Grand Central Dispatch can guarantee that a block submitted to the main queue will run on the main thread, but a block submitted to any other queue has no guarantees about what thread a block will execute on.

No guarantee is made regarding which thread a block will be invoked on; however, it is guaranteed that only one block submitted to the FIFO dispatch queue will be invoked at a time.

Grand Central Dispatch manages a pool of threads and reuses existing threads as much as possible. If the main thread is available for work (i.e. idle) a block can be executed on that thread.

like image 36
quellish Avatar answered Oct 12 '22 07:10

quellish