Dispatch_sync () always executes a block on the main thread

Is there any difference between if dispatch_sync is called on 3 different queues like

1.

 dispatch_sync(dispatch_get_main_queue(),^(void){
      NSLog(@"this execute in main thread") // via [NSThread isMainThread]   

  });

      

2.

dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void){
    NSLog(@"this also execute in main thread")  // via [NSThread isMainThread]
}

      

3.

dispatch_queue_t queue;
queue = dispatch_queue_create("com.example.MyQueue", NULL);
dispatch_sync(queue, ^(void){
    NSLog(@"this also execute in main thread")  // via [NSThread isMainThread]
}

      

Whenever I call dispatch_sync, the block is executed on the main thread without considering which queue it is dispatched to. So why does this function take a queue as an argument as it doesn't use it. Can someone please clarify this?

+3


source to share


3 answers


dispatch_sync

- blocking operation. That is, the function will not return until the work represented in the block is complete.

When dispatched to an asynchronous queue - like one of the global queues or a parallel queue of your own creation - there is no reason to do anything other than call a block on the thread that is called dispatch_sync()

. Even if a block is called in a synchronous queue, it dispatch_sync()

will wait until it completes anyway so, internally, it can also stop until the rest of the work is done in the queue, and then execute the block directly.

It turns out that transferring data from stream A to stream B is expensive. If the queue is in a state where execution can occur immediately, then it dispatch_sync

will speed up execution by simply calling the block on the thread it was called on dispatch_sync

.



And, by definition, you don't care. The calling thread is blocked - cannot do anything - until dispatch_sync()

it returns.

So, really, this is all implementation detail. GCD is free to execute blocks on whichever threads it deems most appropriate. It just so happens that context switching is often the most important rule in understanding this.

+9


source


See the documentation dispatch_sync

which notes

As an optimization, this function calls a block on the current thread whenever possible.

If you are submitting something synchronously, since the thread has to wait for the submitted code to complete, anyway, it will often run that code on the current thread. So if it dispatches synchronously from the main thread, it will run on the main thread. If sent in sync with a background thread, it will run on that background thread.



As noted by ipmcc, a well-known exception is when the background thread sends something in sync with the main thread. As the libdispatch source says:

It prefers to execute synchronous blocks on the current thread due to local side effects, garbage collection, etc. However, blocks passed to the main thread MUST start on the main thread.

+5


source


For your problem: you call dispatch_sync always on the main queue, and if you want to know why, see the following:

First you need to pay attention to the description for "dispatch_sync"

Sends a block to the submit queue for synchronous execution. Unlike dispatch_async, this function does not return until the block completes. Calling this function and targeting the current QUEUE (NOT THREAD) results in blocking.

#define logStep(step,queue) NSLog(@"step: %d at thread: %@ in -- queue: %s",step,[NSThread currentThread],dispatch_queue_get_label(queue));
// call the method in main thread within viewDidLoad or viewWillAppear ...
- (void)testDispatchSync{
    //let distinctly tell the 4 queues we often use at first
    self.concurrentQ = dispatch_queue_create("com.shared.concurrent", DISPATCH_QUEUE_CONCURRENT);
    self.serialQ = dispatch_queue_create("com.shared.serial", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t mainQ = dispatch_get_main_queue();
    dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    logStep(1,mainQ) //we're in main thread and main queue, current queue IS main queue

    // do a sync in main thread & concurrent queue
    dispatch_sync(_concurrentQ, ^{
        logStep(2,_concurrentQ)
    });

    // do a sync in main thread & serial queue
    dispatch_sync(_serialQ, ^{
        logStep(3,_serialQ)
    });

    //uncommenting the following code that you'wll see a crash will occur,  because current queue is main queue
    //    dispatch_sync(mainQ, ^{
    //        logStep(4, mainQ)
    //    });

    dispatch_async(_concurrentQ, ^{
        // inside of the this scope, current queue is "_concurrentQ"
        logStep(11,_concurrentQ)

        // using sync in any queue here will be safe!
        dispatch_sync(_concurrentQ, ^{
            logStep(12,_concurrentQ)
        });
        dispatch_sync(_serialQ, ^{
            logStep(13,_concurrentQ)
        });
        dispatch_sync(mainQ, ^{
            logStep(14,mainQ)
        });
        dispatch_sync(globalQ, ^{
            logStep(15,globalQ)

        });

        // using async in any queue here will be safe!
        dispatch_async(_concurrentQ, ^{
            logStep(111,_concurrentQ)
        });
        dispatch_async(_serialQ, ^{
            logStep(112,_concurrentQ)
        });
        dispatch_async(mainQ, ^{
            logStep(113,mainQ)
        });
        dispatch_async(globalQ, ^{
            logStep(114,globalQ)
        });

    });


    dispatch_async(_serialQ, ^{
        // inside of the this scope, current queue is "_serialQ"
        logStep(21,_serialQ)

        // using async in any queue except current queue here will be safe!
        dispatch_sync(_concurrentQ, ^{
            logStep(22,_concurrentQ)
        });
        dispatch_sync(mainQ, ^{
            logStep(23,mainQ)
        });
        dispatch_sync(globalQ, ^{
            logStep(24,globalQ)
        });

        //uncommenting the following code that you'wll see a crash will occur,  because current queue is "_serialQ"
        //        dispatch_sync(_serialQ, ^{ //app will die at here
        //            logStep(25,_serialQ)
        //        });
    });
}

      

So we concluded that the
key problem is that the thread will be blocked while dispatch_sync is running on the current queue, which is a sequential queue at the same time.
the main queue is also a sequential queue, so it explains why you can't call dispatch_sync on the main thread

0


source







All Articles