GCD untuk melakukan tugas di utas utama

254

Saya memiliki panggilan balik yang mungkin berasal dari utas apa pun. Ketika saya mendapatkan panggilan balik ini, maka saya ingin melakukan tugas tertentu di utas utama.

Apakah saya perlu memeriksa apakah saya sudah berada di utas utama - atau adakah hukuman dengan tidak melakukan pemeriksaan ini sebelum memanggil kode di bawah ini?

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});
Egil
sumber
116
Lima tahun kemudian saya masih tidak dapat mengingat sintaks dari blok GCD dan berakhir di sini setiap saat.
SpaceTrucker
7
@SpaceTrucker - Itulah alasan saya berada di halaman ini: D
Matthew Cawley
4
9 tahun kemudian, dan saya masih datang untuk menyalin sintaks dari halaman ini.
Osa

Jawaban:

152

Tidak, Anda tidak perlu memeriksa apakah Anda sudah berada di utas utama. Dengan mengirimkan blok ke antrian utama, Anda hanya menjadwalkan blok yang akan dieksekusi secara seri pada utas utama, yang terjadi ketika run loop yang sesuai dijalankan.

Jika Anda sudah berada di utas utama, tingkah lakunya sama: blok dijadwalkan, dan dieksekusi ketika loop run dari utas utama dijalankan.


sumber
3
Pertanyaannya adalah apakah ada "penalti dengan tidak melakukan pemeriksaan ini" ... Saya akan berpikir bahwa ada penalti kinerja untuk menggunakan pengiriman async ketika itu tidak diperlukan, atau itu sepele?
Dan Rosenstark
1
@Yar Saya tidak berpikir ada dampak kinerja yang nyata dalam banyak kasus: GCD adalah pustaka yang ringan. Yang mengatakan, saya mengerti pertanyaan sebagai: 'diberi kode di bawah ini, apakah saya perlu memeriksa apakah saya berada di utas utama?'
7
Namun, Anda harus memeriksa apakah Anda menggunakan dispatch_sync. Kalau tidak, Anda akan menemui jalan buntu.
Victor Engel
Jika Anda berada di antrian utama dan mengirim asynckembali ke antrian utama, itu akan dijalankan tetapi itu mungkin mengacaukan waktu yang diharapkan dari tindakan Anda . Seperti kode UI dalam viewDidLoad() tidak berjalan sampai setelah tampilan pertama kali ditampilkan .
pkamb
106

Untuk kasus pengiriman asinkron yang Anda jelaskan di atas, Anda tidak perlu memeriksa apakah Anda berada di utas utama. Seperti yang ditunjukkan oleh Bavarious, ini hanya akan di-antri untuk dijalankan di utas utama.

Namun, jika Anda mencoba melakukan hal di atas menggunakan a dispatch_sync()dan panggilan balik Anda ada di utas utama, aplikasi Anda akan menemui jalan buntu pada saat itu. Saya menjelaskan ini dalam jawaban saya di sini , karena perilaku ini mengejutkan saya ketika bergerak beberapa kode dari -performSelectorOnMainThread:. Seperti yang saya sebutkan di sana, saya membuat fungsi pembantu:

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

yang akan menjalankan blok secara bersamaan pada utas utama jika metode yang Anda gunakan saat ini tidak pada utas utama, dan hanya menjalankan blok inline jika itu. Anda dapat menggunakan sintaks seperti berikut untuk menggunakan ini:

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});
Brad Larson
sumber
1
Mengapa tidak menyebutnya sesuatu seperti "runOnMainQueueSync"? Fakta bahwa itu bisa menemui jalan buntu dan bukan merupakan hal yang saya tidak ingin memiliki seluruh kode saya. Terima kasih dan +1 seperti biasa.
Dan Rosenstark
2
@Yar - Saya baru saja memberikan nama itu sehingga jelas bagi saya mengapa saya tidak hanya menembakkan blok secara serempak pada antrian utama alih-alih menggunakan fungsi pembantu ini. Itu lebih merupakan pengingat bagi diriku sendiri.
Brad Larson
3
Masih mungkin menemui jalan buntu dengan fungsi ini. Sinkronisasi antrian utama> sinkronisasi antrian lainnya> antrian utama akan menemui jalan buntu.
hfossli
2
@ Hossossli - Benar, itu tidak akan menangani setiap kasus. Dalam penggunaan saya, saya selalu memanggil dari pengiriman asinkron pada antrian serial latar belakang atau kode inline pada utas utama. Saya ingin tahu apakah dispatch_set_specific()akan membantu dalam kasus yang Anda jelaskan: stackoverflow.com/a/12806754/19679 .
Brad Larson
1
[NSThread isMainThread] sering mengembalikan YA dan tidak dianggap aman untuk memeriksa kasus ini dalam pemrograman GCD. stackoverflow.com/questions/14716334/…
Will Larche
57

Seperti jawaban lain yang disebutkan, dispatch_async dari utas utama baik-baik saja.

Namun, tergantung pada kasus penggunaan Anda, ada efek samping yang dapat Anda anggap merugikan: karena blok dijadwalkan pada antrian, itu tidak akan dijalankan sampai kontrol kembali ke loop berjalan, yang akan memiliki efek menunda eksekusi blok Anda.

Sebagai contoh,

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

Akan dicetak:

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

Untuk alasan ini, jika Anda mengharapkan blok untuk mengeksekusi di antara NSLog luar, dispatch_async tidak akan membantu Anda.

Michael Chinen
sumber
Perlu untuk mengatakan bahwa ini adalah hal yang penting untuk dipertimbangkan
Marc-Alexandre Bérubé
Karena Anda ingin memeriksa, ini berarti kode dapat berjalan atau tidak di utas utama. Sudah di utas utama akan berjalan dalam urutan itu, jika tidak ini tidak dapat dijamin. Jadi, Anda harus menghindari mendesain kode untuk dijalankan dalam urutan tertentu ketika merujuk ke pemrograman multithread. Cobalah untuk menggunakan cara panggilan balik async.
ooops
1

Tidak, Anda tidak perlu memeriksa apakah Anda berada di utas utama. Inilah cara Anda melakukan ini di Swift:

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

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

Termasuk dalam fungsi standar dalam repo saya, lihat: https://github.com/goktugyil/EZSwiftExtensions

Esqarrouth
sumber