В какой очереди GCD, основной или нет, я работаю?

Я пытаюсь написать некоторые потокобезопасные методы, поэтому я использую:

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

Но если я нахожусь в основном потоке, в этом нет необходимости, и я могу пропустить все эти диспетчерские вызовы, поэтому я хотел бы знать, в каком потоке я сейчас нахожусь. Откуда я могу это знать?

Или, возможно, это не имеет значения (в производительности)?

Можно ли делать это сравнение?

if (dispatch_get_main_queue() == dispatch_get_current_queue()){...}

person nacho4d    schedule 09.02.2011    source источник
comment
Не только меньше накладных расходов с использованием одного из упомянутых методов, но и если вы выполняете dispatch_sync для той же очереди, в которой вы работаете, вы попадете в тупик. dispatch_async будет в порядке.   -  person smparkes    schedule 11.02.2011
comment
Если вам нужно более универсальное решение для предотвращения взаимоблокировок в libdispatch, воспользуйтесь этими помощниками. Они не охватывают main_queue, просто будьте осторожны с этим. gist.github.com/1205760   -  person steipete    schedule 09.09.2011
comment
Вопрос довольно хороший, но пример кода показывает шаблон взаимоблокировки. Может лучше переделать.   -  person xi.lin    schedule 05.05.2016


Ответы (9)


Обновленный ответ:

Документы Apple изменились и теперь говорят: «При вызове вне контекста отправленного блока эта функция возвращает основную очередь, если вызов выполняется из основного потока. Если вызов выполняется из любого другого потока, эта функция возвращает параллельная очередь по умолчанию». поэтому проверка dispatch_get_main_queue() == dispatch_get_current_queue() должна работать.

Исходный ответ:

Использование dispatch_get_main_queue() == dispatch_get_current_queue() не сработает. В документах для dispatch_get_current_queue говорится: «При вызове вне контекста отправленного блока эта функция возвращает параллельную очередь по умолчанию». Параллельная очередь по умолчанию не является основной очередью.

[NSThread isMainThread] должно работать на то, что вы хотите. Обратите внимание, что [NSThread isMainThread] может быть истинным для очередей, отличных от основной очереди, например, при вызове dispatch_sync из основного потока.

person smparkes    schedule 11.02.2011
comment
На самом деле там написано, что при вызове вне контекста отправленного блока эта функция возвращает основную очередь, если вызов выполняется из основного потока. Если вызов сделан из любого другого потока, эта функция возвращает параллельную очередь по умолчанию. - person mikelikespie; 25.10.2011
comment
Большое спасибо за обновление. Я думаю, что это то, как мы все ожидаем, что это будет работать :) - person nacho4d; 25.10.2011
comment
Похоже, что dispatch_get_current_queue() устарел, какая альтернатива? - person Shizam; 12.04.2013
comment
вы должны использовать dispatch_get_specific. См. stackoverflow.com/a/15725847/412916 и ссылку на форумы Apple в комментариях. - person Jano; 16.09.2013

С амортизацией dispatch_get_current_queue() эквивалентно (dispatch_get_main_queue() == dispatch_get_current_queue())

теперь выполняется сравнением меток очереди, которое:

(dispatch_queue_get_label(dispatch_get_main_queue()) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))
person ambientlight    schedule 08.01.2016
comment
Обратите внимание, что dispatch_queue_get_label() возвращает char *. Достаточно ли проверить значения указателя, возвращаемые обоими вызовами? Является ли вызов strcmp() избыточным? - person Bemipefe; 29.03.2020

Обновленный ответ:

dispatch_get_current_queue() теперь устарела.

Исходный ответ:

В OS-X 10.8 в заголовочном файле (queue.h) в комментарии над функцией dispatch_get_current_queue() написано:

Когда в основном потоке вызывается dispatch_get_current_queue(), он может возвращать или не возвращать то же значение, что и dispatch_get_main_queue(). Их сравнение не является допустимым способом проверить, выполняется ли код в основном потоке.

Я обнаружил, что из-за

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

в моем коде, который хорошо работает на iOS, но не работает на OS-X.

person Gabriele Mondada    schedule 23.11.2012

Новое для iOS: 10.0+ и macOS: 10.12+ (tvOS: 10.0+, watchOS: 3.0+).

Свифт 4+:

Проверьте, работает ли он в основной очереди:

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

Утвердить, если выполняется в основной очереди:

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

Утверждать, если НЕ выполняется в основной очереди:

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

ObjC: проверьте, выполняется ли в основной очереди:

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

Утвердить, если выполняется в основной очереди:

dispatch_assert_queue(dispatch_get_main_queue());
   

Утверждать, если НЕ выполняется в основной очереди:

dispatch_assert_queue_not(dispatch_get_main_queue());

ПРИМЕЧАНИЕ. mainThread != mainQueue Основная очередь всегда выполняется в основном потоке, но очередь может выполняться в основном потоке, но не быть основной очередью. Итак, не смешивайте тесты потоков и очередей!

person Sverrisson    schedule 31.01.2019

Если вы находитесь в Objective-C и хотите, чтобы что-то происходило в основном потоке синхронно, не проще ли было бы использовать

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

Это имеет то преимущество, что если вы уже находитесь в основном потоке, это не имеет значения, сообщение отправляется обычным способом.

person JeremyP    schedule 09.02.2011

Использовать dispatch_get_current_queue() небезопасно, за исключением того, что вы не выполняете отладку. Как ясно написано на dispatch_queue справочной странице:

ПРЕДОСТЕРЕЖЕНИЯ

Код не может делать никаких предположений об очереди, возвращаемой dispatch_get_current_queue(). Возвращаемая очередь может иметь произвольные политики, которые могут застать врасплох код, пытающийся запланировать работу с очередью. Список политик включает, помимо прочего, ширину очереди (т. е. последовательную или одновременную), приоритет планирования, учетные данные безопасности или конфигурацию файловой системы. Поэтому dispatch_get_current_queue() ДОЛЖЕН использоваться только для проверки подлинности или отладки.

Лучше (может быть и сложнее) синхронизировать фоновые потоки:

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

Возможно, я совершенно не прав, но я предлагаю именно это.

person Madhup Singh Yadav    schedule 09.02.2011
comment
Ясно заявляет, что для проверки личности все в порядке. Это то, на что намекал первоначальный вопрос (хотя это не сработает, как я уже упоминал в своем ответе). - person smparkes; 11.02.2011

На самом деле, это нормально для использования.

В документации говорится

При вызове вне контекста отправленного блока эта функция возвращает основную очередь, если вызов выполняется из основного потока. Если вызов сделан из любого другого потока, эта функция возвращает параллельную очередь по умолчанию.

person mikelikespie    schedule 24.10.2011

Обратите внимание, что основная очередь не совпадает с основным потоком. Неосновная очередь легко может работать в основном потоке, когда dispatch_sync() используется в очереди из основного потока. Гораздо реже в инструментах командной строки, которые используют dispatch_main() вместо NSRunLoops (т. е. в приложениях, отличных от Cocoa/iOS), основная очередь может выполняться не в основном потоке, а во что-то другом.

Если вы имеете дело с кодом, которому требуется основная очередь, а не только основной поток (скажем, код, который ожидает значения, установленные в основной очереди из queue_get_specific, что, по слухам, делает VectorKit/MapKit), лучше проверить наличие основная очередь явно, а не основной поток.

Один из вариантов явной проверки основной очереди:

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;
}

Ответ с использованием DISPATCH_CURRENT_QUEUE_LABEL также должен работать и, вероятно, лучше, хотя может не работать (т.е. сбой) до MacOS 10.9 и iOS7, когда был определен DISPATCH_CURRENT_QUEUE_LABEL.

person Carl Lindberg    schedule 26.05.2018

dispatch_get_current_queue устарело, начиная с iOS 6.0. Похоже, [NSThread isMainThread] — единственный выход.

person Andrei Tchijov    schedule 27.08.2013
comment
Я видел использование dispatch_get_main_queue во многих учебниках и документации от Apple и третьих лиц. Кажется, это очень широко используемая функция. Поэтому я оспариваю, что это устарело в целом. Можешь дать ссылки? Существует ли стратегия прямой замены? - person Daniel S.; 28.08.2013
comment
вы правы, я неправильно вырезал/вставлял имя функции. это dispatch_get_current_queue, который устарел. Извините за недопонимание. Тем не менее, вывод остается в силе. [NSThread isMainThread] — единственный путь (хотя это не всегда означает, что вы находитесь в основной очереди). - person Andrei Tchijov; 28.08.2013
comment
Ага, тоже ново для меня, но да, согласен. :) - person Daniel S.; 28.08.2013