Verifique se o segmento atual é ou não o segmento principal


121

Existe alguma maneira de verificar se o thread atual é ou não o thread principal no Objective-C?

Eu quero fazer algo assim.

  - (void)someMethod
  {
    if (IS_THIS_MAIN_THREAD?) {
      NSLog(@"ok. this is main thread.");
    } else {
      NSLog(@"don't call this method from other thread!");
    }
  }

o que há de errado em chamar um método de outros threads?
David天宇Wong

Respostas:



24

Se você deseja que um método seja executado no encadeamento principal, você pode:

- (void)someMethod
{
    dispatch_block_t block = ^{
        // Code for the method goes here
    };

    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_async(dispatch_get_main_queue(), block);
    }
}

5
As respostas a perguntas antigas podem se beneficiar de uma explicação de como a nova resposta difere das respostas existentes.
Jason Aller

1
Isso é um exagero, se algum trabalho deve ser feito no encadeamento principal, não adianta verificar se você está no encadeamento principal ou não. Apenas façaNSOperationQueue.mainQueue().addOperationWithBlock { //your work here }
Eric

3
@ Eric, eu concordo, mas e se você quiser execução imediata do método se já estiver no thread principal? Na sua sugestão, o método é sempre despachado para ser executado posteriormente pela fila de operação principal.
precisa saber é

@ boherna correto, isso é algo a se observar.
Eric

@boherna Comentário tardio, mas o argumento que você coloca no seu comentário seria mais forte se você usar em dispatch_sync()vez de dispatch_async()no seu exemplo.
Caleb


13

Se você quiser saber se está ou não no segmento principal, basta usar o depurador. Defina um ponto de interrupção na linha em que você está interessado e, quando seu programa chegar, chame isto:

(lldb) thread info

Isso exibirá informações sobre o segmento em que você está:

(lldb) thread info thread #1: tid = 0xe8ad0, 0x00000001083515a0 MyApp`MyApp.ViewController.sliderMoved (sender=0x00007fd221486340, self=0x00007fd22161c1a0)(ObjectiveC.UISlider) -> () + 112 at ViewController.swift:20, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1

Se o valor for queuefor com.apple.main-thread, você estará no segmento principal.


6

O padrão a seguir garantirá que um método seja executado no encadeamento principal:

- (void)yourMethod {
    // make sure this runs on the main thread 
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:_cmd/*@selector(yourMethod)*/
                               withObject:nil
                            waitUntilDone:YES];
        return;
    }
    // put your code for yourMethod here
}

_cmdusará automaticamente o método em que seu snippet é colado em ʔ • ᴥ • ʔ
Albert Renshaw

3

Dois caminhos. Da resposta de @ rano,

[[NSThread currentThread] isMainThread] ? NSLog(@"MAIN THREAD") : NSLog(@"NOT MAIN THREAD");

Além disso,

[[NSThread mainThread] isEqual:[NSThread currentThread]] ? NSLog(@"MAIN THREAD") : NSLog(@"NOT MAIN THREAD");

3
void ensureOnMainQueue(void (^block)(void)) {

    if ([[NSOperationQueue currentQueue] isEqual:[NSOperationQueue mainQueue]]) {

        block();

    } else {

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{

            block();

        }];

    }

}

note que eu verifico a fila de operações, não o thread, pois essa é uma abordagem mais segura


Esta deve ser a resposta aceita, segmento principal = principal fila!
railwayparade

2

Para o Monotouch / Xamarin iOS, você pode executar a verificação desta maneira:

if (NSThread.Current.IsMainThread)
{
    DoSomething();
}
else
{
    BeginInvokeOnMainThread(() => DoSomething());
}

1

Versão rápida


if (NSThread.isMainThread()) {
    print("Main Thread")
}


0

Detalhes

  • Swift 5.1, Xcode 11.3.1

Solução 1. Detecte qualquer fila

Obter o DispatchQueue atual?

Solução 2. Detectar apenas a fila principal

import Foundation

extension DispatchQueue {

    private struct QueueReference { weak var queue: DispatchQueue? }

    private static let key: DispatchSpecificKey<QueueReference> = {
        let key = DispatchSpecificKey<QueueReference>()
        let queue = DispatchQueue.main
        queue.setSpecific(key: key, value: QueueReference(queue: queue))
        return key
    }()

    static var isRunningOnMainQueue: Bool { getSpecific(key: key)?.queue == .main }
}

Uso

if DispatchQueue.isRunningOnMainQueue { ... }

Amostra

func test(queue: DispatchQueue) {
    queue.async {
        print("--------------------------------------------------------")
        print("queue label: \(queue.label)")
        print("is running on main queue: \(DispatchQueue.isRunningOnMainQueue)")
    }
}

test(queue: DispatchQueue.main)
sleep(1)
test(queue: DispatchQueue.global(qos: .background))
sleep(1)
test(queue: DispatchQueue.global(qos: .unspecified))

Resultado (log)

--------------------------------------------------------
queue label: com.apple.root.background-qos
is running on main queue: false
--------------------------------------------------------
queue label: com.apple.root.default-qos
is running on main queue: false
--------------------------------------------------------
queue label: com.apple.main-thread
is running on main queue: true

0
Here is a way to detect what the current queue is
extension DispatchQueue {
    //Label of the current dispatch queue.
    static var currentQueueLabel: String { String(cString: __dispatch_queue_get_label(nil)) }

    /// Whether the current queue is a `NSBackgroundActivityScheduler` task.
    static var isCurrentQueueNSBackgroundActivitySchedulerQueue: Bool { currentQueueLabel.hasPrefix("com.apple.xpc.activity.") }

    /// Whether the current queue is a `Main` task.
    static var isCurrentQueueMainQueue: Bool { currentQueueLabel.hasPrefix("com.apple.main-thread") }
}

-2

UPDATE: parece que a solução não está correta, de acordo com o cabeçalho queue.h, como mencionado @demosten

O primeiro pensamento foi trazido a mim, quando eu precisava dessa funcionalidade:

dispatch_get_main_queue() == dispatch_get_current_queue();

E procurou a solução aceita:

[NSThread isMainThread];

solução da mina 2,5 vezes mais rápido.

PS E sim, eu verifiquei, funciona para todos os threads


3
Faz sentido - seu método ignora a sobrecarga do sistema de mensagens de tempo de execução obj-c. Embora se você estiver usando essa técnica, eu diria que tem um cheiro ruim de código, talvez o de otimização prematura.
ArtOfWarfare

4
dispatch_get_current_queue () está obsoleto do iOs 6.0
Durai Amuthan.H 21/10

33
Você pode ler isso na descrição do cabeçalho queue.h da Apple, em que dispatch_get_current_queue () está definido: 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.
demosten
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.