GCD para executar tarefas no thread principal


254

Eu tenho um retorno de chamada que pode vir de qualquer thread. Quando recebo esse retorno de chamada, gostaria de executar uma determinada tarefa no thread principal.

Preciso verificar se já estou no segmento principal - ou há alguma penalidade por não executar essa verificação antes de chamar o código abaixo?

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});

116
Cinco anos depois, ainda não consigo me lembrar da sintaxe dos blocos do GCD e acabo aqui sempre.
SpaceTrucker

7
@SpaceTrucker - É a mesma razão que estou nesta página: D
Matthew Cawley 28/08

4
9 anos depois, e continuo copiando a sintaxe desta página.
Osa

Respostas:


152

Não, você não precisa verificar se você já está no segmento principal. Ao despachar o bloco para a fila principal, você está apenas programando o bloco para ser executado em série no encadeamento principal, o que acontece quando o loop de execução correspondente é executado.

Se você já está no segmento principal, o comportamento é o mesmo: o bloco é agendado e executado quando o loop de execução do segmento principal é executado.


3
A pergunta era se existe uma "penalidade por não executar essa verificação" ... Eu acho que há uma penalidade de desempenho por usar o envio assíncrono quando não é necessário ou é trivial?
Dan Rosenstark

1
@Yar Não acho que haja um impacto perceptível no desempenho na maioria dos casos: o GCD é uma biblioteca leve. Dito isto, entendi a pergunta como: 'dado o código abaixo, preciso verificar se estou no segmento principal?'

7
Você, no entanto, precisa verificar se usa o dispatch_sync. Caso contrário, você receberá um impasse.
Victor Engel

Se você estiver na fila principal e despachar de asyncvolta para a fila principal, ela será executada, mas isso pode atrapalhar o tempo esperado de suas ações . Tais como o código UI em viewDidLoad() não correr até após a exibição é exibido pela primeira vez .
Pkamb #

106

Para o caso de envio assíncrono descrito acima, não é necessário verificar se você está no thread principal. Como indica Bavarious, isso simplesmente será colocado na fila para ser executado no thread principal.

No entanto, se você tentar fazer o acima usando dispatch_sync()ae seu retorno de chamada estiver no thread principal, seu aplicativo entrará em conflito nesse momento. Eu descrevo isso na minha resposta aqui , porque esse comportamento me surpreendeu ao mover algum código de -performSelectorOnMainThread:. Como mencionei lá, criei uma função auxiliar:

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

que executará um bloco de forma síncrona no segmento principal se o método em que você está atualmente não estiver no segmento principal e apenas executará o bloco em linha, se estiver. Você pode empregar sintaxe como a seguinte para usar isso:

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

1
Por que não chamar algo como "runOnMainQueueSync"? O fato de que ele pode entrar em conflito e não é o tipo de coisa que eu não gostaria de ter em todo o meu código. Obrigado e +1 como sempre.
Dan Rosenstark

2
@ Yar - acabei de dar esse nome para que fique claro por que eu não estava disparando blocos de forma síncrona na fila principal, em vez de usar essa função auxiliar. Foi mais um lembrete para mim mesma.
Brad Larson

3
Ainda é possível entrar em conflito com esta função. Sincronização na fila principal> sincronização em outra fila> fila principal entrará em impasse.
Hfossli #

2
@ hfossli - É verdade que não vai lidar com todos os casos. No meu uso, eu estava sempre ligando de uma expedição assíncrona em uma fila serial em segundo plano ou em um código embutido no thread principal. Gostaria de saber se dispatch_set_specific()ajudaria no caso de você descrever: stackoverflow.com/a/12806754/19679 .
Brad Larson

1
[NSThread isMainThread] geralmente retorna YES e não é considerado seguro para verificar esse caso na programação GCD. stackoverflow.com/questions/14716334/…
Will Larche

57

Como as outras respostas mencionadas, o dispatch_async do thread principal está correto.

No entanto, dependendo do seu caso de uso, há um efeito colateral que você pode considerar uma desvantagem: como o bloco está agendado em uma fila, ele não será executado até que o controle volte ao loop de execução, o que atrasará a execução do seu bloco.

Por exemplo,

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

Irá imprimir:

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

Por esse motivo, se você esperava que o bloco fosse executado entre os NSLog externos, o dispatch_async não ajudaria.


Necessidades de dizer que esta é uma coisa importante a considerar
Marc-Alexandre Bérubé

Como você deseja verificar, isso significa que o código pode ou não ser executado no thread principal. Já no thread principal será executado nessa ordem, caso contrário, isso não pode ser garantido. Portanto, você deve evitar projetar o código para executar em uma ordem específica quando se referir à programação multithread. Tente usar a maneira de retorno de chamada assíncrona.
ooops

1

Não, você não precisa verificar se está no tópico principal. Aqui está como você pode fazer isso no Swift:

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

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

Está incluído como uma função padrão no meu repositório, confira: https://github.com/goktugyil/EZSwiftExtensions

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.