Ana iş parçacığında görev yapmak için GCD


255

Herhangi bir iş parçacığından gelebilir bir geri arama var. Bu geri aramayı aldığımda, ana iş parçacığında belirli bir görevi gerçekleştirmek istiyorum.

Zaten ana iş parçacığı üzerinde olup olmadığımı kontrol etmek gerekiyor mu - ya da aşağıdaki kodu çağırarak bu kontrolü yapmadan herhangi bir ceza var mı?

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

117
Beş yıl sonra hala GCD bloklarının sözdizimini hatırlayamıyorum ve her seferinde buraya geliyorum.
SpaceTrucker

7
@SpaceTrucker - Bu yüzden bu sayfadayım: D
Matthew Cawley

4
9 yıl sonra hala sözdizimini bu sayfadan kopyalamaya geldim.
Osa

Yanıtlar:


153

Hayır, ana başlıkta olup olmadığınızı kontrol etmeniz gerekmez. Bloğu ana kuyruğa göndererek, bloğun ana iş parçacığı üzerinde seri olarak çalıştırılmasını planlıyorsunuz ve ilgili çalıştırma döngüsü çalıştırıldığında gerçekleşiyor.

Zaten ana iş parçacığındaysanız, davranış aynıdır: blok zamanlanır ve ana iş parçacığının çalışma döngüsü çalıştırıldığında yürütülür.


3
Soru, "bu kontrolü yerine getirmeme cezası" olup olmadığıydı ... Gerekmediğinde zaman uyumsuz dağıtımı kullanmak için bir performans cezası olduğunu düşünüyor muydum yoksa önemsiz mi?
Dan Rosenstark

1
@Yar Çoğu durumda gözle görülür bir performans etkisi olduğunu düşünmüyorum: GCD hafif bir kütüphanedir. Bununla birlikte, soruyu şu şekilde anladım: 'Aşağıdaki kod verildiğinde, ana iş parçacığında olup olmadığımı kontrol etmem gerekir mi?'

7
Ancak, dispatch_sync kullanıp kullanmadığınızı kontrol etmeniz gerekir. Aksi takdirde bir kilitlenme oluşur.
Victor Engel

Ana kuyruğa girip ana kuyruğa asyncgeri gönderirseniz çalıştırılır, ancak bu işlemlerinizin beklenen zamanlamasını bozabilir . Bu UI kodu olarak viewDidLoad() kadar çalışmıyorsa sonra görünüşüdür ilk olarak görüntülenir .
pkamb

106

Yukarıda açıkladığınız eşzamansız gönderim durumunda, ana iş parçacığında olup olmadığınızı kontrol etmeniz gerekmez. Bavyeralı'nın belirttiği gibi, bu sadece ana iş parçacığında çalıştırılmak üzere sıraya alınacaktır.

Ancak, a'yı kullanarak yukarıdaki işlemleri yapmaya çalışırsanız dispatch_sync()ve geri aramanız ana iş parçacığındaysa, uygulamanız o noktada kilitlenecektir. Bunu burada cevabımda açıklıyorum , çünkü bu davranış bazı kodları taşırken beni şaşırttı -performSelectorOnMainThread:. Orada bahsettiğim gibi, bir yardımcı fonksiyon oluşturdum:

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

bu, bulunduğunuz yöntem şu anda ana iş parçacığı üzerinde değilse, ana iş parçacığı üzerinde eşzamanlı olarak bir blok çalıştıracak ve yalnızca varsa, satır içi satır yürütür. Bunu kullanmak için aşağıdakine benzer bir sözdizimi kullanabilirsiniz:

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

1
Neden "runOnMainQueueSync" gibi bir şey demiyorsunuz? Kilitlenebileceği ve yapamayacağı gerçeği, kodumun her yerinde olmasını istemediğim bir şey. Teşekkürler ve her zamanki gibi +1.
Dan Rosenstark

2
@Yar - Bu adı verdim, bu yüzden neden bu yardımcı işlevi kullanmak yerine ana kuyrukta blokları eşzamanlı olarak ateşlemediğimi netleştirdim. Bu daha çok hatırlatmamdı.
Brad Larson

3
Bu işlevle hala kilitlenme mümkündür. Ana kuyruk senkronizasyonu> diğer kuyruk senkronizasyonu> ana kuyruk kilitlenecektir.
hfossli

2
@hfossli - Doğru, her durumla başa çıkmayacak. Benim kullanımımda, ben her zaman bir arka plan seri kuyruğunda asenkron bir sevk ya da ana iş parçacığı üzerinde satır içi kod çağırıyordu. Açıkladığınız dispatch_set_specific()durumda size yardımcı olup olmayacağını merak ediyorum : stackoverflow.com/a/12806754/19679 .
Brad Larson

1
[NSThread isMainThread] genellikle EVET döndürür ve GCD programlamasında bu durumu kontrol etmek için güvenli kabul edilmez. stackoverflow.com/questions/14716334/…
Will Larche

57

Belirtilen diğer cevaplar gibi, ana iş parçacığından dispatch_async gayet iyi.

Bununla birlikte, kullanım durumunuza bağlı olarak, bir dezavantaj olduğunu düşünebileceğiniz bir yan etki vardır: blok bir kuyrukta zamanlandığı için, kontrol geciktirme etkisi olacak olan çalıştırma döngüsüne geri dönene kadar yürütülmez. bloğunuzun yürütülmesi.

Örneğin,

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

Yazdırılacak:

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

Bu nedenle, bloğun dış NSLog'lar arasında yürütülmesini bekliyorsanız, dispatch_async size yardımcı olmaz.


Bunun dikkate alınması gereken önemli bir şey olduğunu söylemeliyim
Marc-Alexandre Bérubé

Kontrol etmek istediğiniz için bu, kodun ana iş parçacığında çalışabileceği veya çalışmayabileceği anlamına gelir. Zaten ana iş parçacığında bu sırayla çalışır, aksi takdirde bu garanti edilemez. Bu nedenle , çok iş parçacıklı programlamaya başvururken kodu belirli bir sırayla çalışacak şekilde tasarlamaktan kaçınmalısınız . Zaman uyumsuz geri arama yolunu kullanmayı deneyin.
ooops

1

Hayır, ana başlıkta olup olmadığınızı kontrol etmeniz gerekmez. Swift'te bunu nasıl yapabileceğiniz aşağıda açıklanmıştır:

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

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

Repouma standart bir işlev olarak dahil edildi, bir göz atın: https://github.com/goktugyil/EZSwiftExtensions

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.