开发者

What GCD queue, main or not, am I running on?

I am trying to write some thread safe methods so I am using:

...
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_sync(main,^{
  [self doSomethingInTheForeground];
});
...

But If I am on the main thread that is not necessary, and I can skip all those dispatch calls, so I would like to know what thread I currently am on. How can I know this?

Or, perhaps it does not make difference (in performance) doing it?

Is it ok to do this comparison?

if (dispatch_get_main开发者_开发知识库_queue() == dispatch_get_current_queue()){...}


Updated answer:

The Apple docs have changed and now say "When called from outside of the context of a submitted block, this function returns the main queue if the call is executed from the main thread. If the call is made from any other thread, this function returns the default concurrent queue." so checking dispatch_get_main_queue() == dispatch_get_current_queue() should work.

Original answer:

Using dispatch_get_main_queue() == dispatch_get_current_queue() won't work. The docs for dispatch_get_current_queue say "When called outside of the context of a submitted block, this function returns the default concurrent queue". The default concurrent queue is not the main queue.

[NSThread isMainThread] should work for what you want. Note that [NSThread isMainThread] can be true for for queues other than the main queue though, e.g., when calling dispatch_sync from the main thread.


With depreciation of dispatch_get_current_queue(), the equivalent of (dispatch_get_main_queue() == dispatch_get_current_queue())

is now by queue label comparison which is:

(dispatch_queue_get_label(dispatch_get_main_queue()) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))


Updated answer:

dispatch_get_current_queue() is now deprecated.

Original answer:

On OS-X 10.8, in the header file (queue.h), in the comment above the dispatch_get_current_queue() function, it is written:

When dispatch_get_current_queue() is called on the main thread, it may or may not return the same value as dispatch_get_main_queue(). Comparing the two is not a valid way to test whether code is executing on the main thread.

I discovered that because of an

assert(dispatch_get_current_queue() == dispatch_get_main_queue());

in my code that works well on iOS but fails on OS-X.


New for iOS: 10.0+ and macOS: 10.12+ (tvOS: 10.0+, watchOS:3.0+).

Swift 4+:

Check if running on main queue:

if (RunLoop.current == RunLoop.main) {
    print("On main queue")
}
   

Assert if running on main queue:

dispatchPrecondition(condition: .onQueue(.main))
// Code running on main queue
 

Assert if NOT running on main queue:

dispatchPrecondition(condition: .notOnQueue(.main))
// Code NOT running on main queue

ObjC: Check if running on main queue:

if ([[NSRunLoop currentRunLoop] isEqual: [NSRunLoop mainRunLoop]]) {
    NSLog(@"Running on main");
} else {
    NSLog(@"Not running on main");
}
   

Assert if running on main queue:

dispatch_assert_queue(dispatch_get_main_queue());
   

Assert if NOT running on main queue:

dispatch_assert_queue_not(dispatch_get_main_queue());

NOTE: mainThread != mainQueue The main queue always runs on the main thread, but a queue may be running on the main thread without being the main queue. So, don't mix thread and queue tests!


If you are in Objective-C and you want something to happen on the main thread synchronously, would it not be simpler to use

[self performSelectorOnMainThread: @selector(doSomethingInTheForeground) 
                       withObject: nil 
                    waitUntilDone: YES];

This has the advantage that, if you are already on the main thread, it doesn't matter, the message is sent in the normal way.


It's not safe to use dispatch_get_current_queue() except you are not debugging. As it is clearly written in the dispatch_queue man page:

CAVEATS

Code cannot make any assumptions about the queue returned by dispatch_get_current_queue(). The returned queue may have arbitrary policies that may surprise code that tries to schedule work with the queue. The list of policies includes, but is not limited to, queue width (i.e. serial vs. concurrent), scheduling priority, security credential or filesystem configuration. Therefore, dispatch_get_current_queue() MUST only be used for identity tests or debugging.

The better thing (may be more complicated) is to sync the background threads:

dispatch_sync(backgroundqueue,^{
  [self doSomethingInTheBackground];
});

Maybe I am totally wrong, but that is what I suggest.


Actually, it is OK to use.

The documentation says

When called from outside of the context of a submitted block, this function returns the main queue if the call is executed from the main thread. If the call is made from any other thread, this function returns the default concurrent queue.


Note that the main queue is not the same as the main thread. It is easily possible for a non-main queue to be operating on the main thread, when dispatch_sync() is used on a queue from the main thread. Much more rarely, in command-line tools which use dispatch_main() in lieu of NSRunLoops (i.e., other than Cocoa/iOS applications), it's possible for the main queue to execute in something other than the main thread.

If you are dealing with code that requires the main queue and not just the main thread (say code which expects values it set on the main queue from queue_get_specific, which VectorKit/MapKit have been rumored to do), it is better to check for the main queue explicitly rather than the main thread.

One option to explicitly check for the main queue:

BOOL MyIsMainQueue(void)
{
    static char MAIN_IND_KEY;
    static char MAIN_IND_VAL;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        dispatch_queue_set_specific(dispatch_get_main_queue(), &MAIN_IND_KEY, &MAIN_IND_VAL, NULL);
    });

    return dispatch_get_specific(&MAIN_IND_KEY) == &MAIN_IND_VAL;
}

The answer using DISPATCH_CURRENT_QUEUE_LABEL should also work and is probably better, though may not work (i.e. crash) before MacOS 10.9 and iOS7, when DISPATCH_CURRENT_QUEUE_LABEL was defined.


dispatch_get_current_queue is deprecated as of iOS 6.0. Looks like [NSThread isMainThread] is the only way to go.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜