GCD to perform task in main thread
Asked Answered
C

4

288

I have a callback which might come from any thread. When I get this callback, then I would like to perform a certain task on the main thread.

Do I need to check whether I already am on the main thread - or is there any penalty by not performing this check before calling the code below?

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});
Colt answered 14/4, 2011 at 11:14 Comment(6)
Five years later I still can't remember the syntax of the GCD blocks and end up here everytime.Kranz
@Kranz - Thats the same reason I'm on this page :DHeadrick
9 years later, and I still come to copy the syntax from this page.Infancy
And the code to copy is in the question and not in answer! This is why asking question is so important.Lashawn
:))) almost 10 years later...Monovalent
9 Years 11 months, now I write in SWIFT but have to update my Obj C pages from time to time.Diaz
K
167

No, you do not need to check whether you’re already on the main thread. By dispatching the block to the main queue, you’re just scheduling the block to be executed serially on the main thread, which happens when the corresponding run loop is run.

If you already are on the main thread, the behaviour is the same: the block is scheduled, and executed when the run loop of the main thread is run.

Katzir answered 14/4, 2011 at 12:18 Comment(4)
Question was whether there is a "penalty by not performing this check"... I would think that there's a performance penalty for using async dispatch when it's not necessary, or is it trivial?Madelyn
@Yar I don’t think there’s a noticeable performance impact in most cases: GCD is a lightweight library. That said, I understood the question as: ‘given the code below, do I need to check whether I’m on the main thread?’Katzir
You DO, however, need to check if you use dispatch_sync. Otherwise you get a deadlock.Seigniorage
If you're on the main queue and dispatch async back onto the main queue, it will be run but that may mess up the expected timing of your actions. Such as UI code in viewDidLoad() not running until after the view is first displayed.Forsta
P
112

For the asynchronous dispatch case you describe above, you shouldn't need to check if you're on the main thread. As Bavarious indicates, this will simply be queued up to be run on the main thread.

However, if you attempt to do the above using a dispatch_sync() and your callback is on the main thread, your application will deadlock at that point. I describe this in my answer here, because this behavior surprised me when moving some code from -performSelectorOnMainThread:. As I mention there, I created a helper function:

void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}

which will run a block synchronously on the main thread if the method you're in isn't currently on the main thread, and just executes the block inline if it is. You can employ syntax like the following to use this:

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});
Palladin answered 18/11, 2011 at 17:13 Comment(6)
Why not call it something like "runOnMainQueueSync"? The fact that it could deadlock and does not is the kind of thing I wouldn't want to have all over my code. Thanks and +1 as always.Madelyn
@Yar - I just gave it that name so that it was clear to me why I wasn't just firing off blocks synchronously on the main queue instead of using this helper function. It was more of a reminder to myself.Palladin
It is still possible to deadlock with this function. Main-queue sync> other-queue sync> main-queue will deadlock.Housekeeper
@Housekeeper - True, it won't handle every case. In my usage, I was always either calling in from an asynchronous dispatch on a background serial queue or inline code on the main thread. I wonder if dispatch_set_specific() would help in the case you describe: https://mcmap.net/q/109905/-how-can-i-verify-that-i-am-running-on-a-given-gcd-queue-without-using-dispatch_get_current_queue .Palladin
[NSThread isMainThread] often returns YES and isn't considered safe for checking for this case in GCD programming. #14716834Zinkenite
@WillLarche - In this case, it is safe. The whole point of the above is to guarantee synchronous execution of a particular block on the main thread. If this function is run on the main thread, even if it is not on the main queue proper (the case you point to there), -isMainThread will return YES, and the block will run in-line as it should. It guarantees execution on the main thread, which preventing against the most direct deadlock cases (not all, as indicated above, but the ones I encounter in my code).Palladin
K
69

As the other answers mentioned, dispatch_async from the main thread is fine.

However, depending on your use case, there is a side effect that you may consider a disadvantage: since the block is scheduled on a queue, it won't execute until control goes back to the run loop, which will have the effect of delaying your block's execution.

For example,

NSLog(@"before dispatch async");
dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"inside dispatch async block main thread from main thread");
});
NSLog(@"after dispatch async");

Will print out:

before dispatch async
after dispatch async
inside dispatch async block main thread from main thread

For this reason, if you were expecting the block to execute in-between the outer NSLog's, dispatch_async would not help you.

Kendy answered 17/1, 2015 at 0:15 Comment(2)
Needs to say that this is an important thing to considerBritnibrito
Since you want to check, this means the code may or may not run in main thread. Already in main thread will run in that order, otherwise this can not be guaranteed. So you should avoid designing the code to run in specific order when refer to multithread programming. Try to use the async callback way.Wellborn
S
1

No you don't need to check if you're in the main thread. Here is how you can do this in Swift:

runThisInMainThread { () -> Void in
    runThisInMainThread { () -> Void in
        // No problem
    }
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

Its included as a standard function in my repo, check it out: https://github.com/goktugyil/EZSwiftExtensions

Sherborne answered 6/12, 2015 at 3:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.