iPhone - Utas utama Grand Central Dispatch

145

Saya telah menggunakan dengan sukses, pengiriman grand central di aplikasi saya, tetapi saya bertanya-tanya apa keuntungan sebenarnya dari menggunakan sesuatu seperti ini:

dispatch_async(dispatch_get_main_queue(), ^{ ... do stuff

atau bahkan

dispatch_sync(dispatch_get_main_queue(), ^{ ... do stuff

Maksud saya, dalam kedua kasus Anda menembakkan blok yang akan dieksekusi pada utas utama, tepat di mana aplikasi berjalan dan ini tidak akan membantu mengurangi beban. Dalam kasus pertama, Anda tidak memiliki kontrol saat blok akan berjalan. Saya telah melihat kasus blok dieksekusi setengah detik setelah Anda memecat mereka. Kasus kedua, mirip dengan

[self doStuff];

Baik?

Aku ingin tahu apa yang kalian pikirkan.

bebek
sumber
9
Omong-omong, melempar antrian utama ke dispatch_sync akan menghasilkan kebuntuan.
Brooks Hanes
5
Cukup baca di dokumen: "Tidak seperti dispatch_async, [dispatch_sync] tidak kembali sampai blok selesai. Memanggil fungsi ini dan menargetkan antrian saat ini menghasilkan jalan buntu." ... Tapi mungkin saya membaca ini salah ... ( antrian saat ini tidak berarti utas utama). Harap perbaiki jika saya salah.
Brooks Hanes
4
@BrooksHanes tidak selalu benar. Ini akan menghasilkan kebuntuan jika Anda sudah di utas utama. Jika tidak maka tidak akan ada jalan buntu. Lihat sini
Madu

Jawaban:

296

Pengiriman blok ke antrian utama biasanya dilakukan dari antrian latar belakang untuk memberi sinyal bahwa beberapa pemrosesan latar belakang telah selesai misalnya

- (void)doCalculation
{
    //you can use any string instead "com.mycompany.myqueue"
    dispatch_queue_t backgroundQueue = dispatch_queue_create("com.mycompany.myqueue", 0);

    dispatch_async(backgroundQueue, ^{
        int result = <some really long calculation that takes seconds to complete>;

        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateMyUIWithResult:result];
        });    
    });
}

Dalam hal ini, kami melakukan perhitungan panjang pada antrian latar belakang dan perlu memperbarui UI kami ketika perhitungan selesai. Memperbarui UI biasanya harus dilakukan dari antrian utama sehingga kami 'memberi sinyal' kembali ke antrian utama menggunakan dispatch_async bersarang kedua.

Mungkin ada contoh lain di mana Anda mungkin ingin mengirim kembali ke antrian utama tetapi umumnya dilakukan dengan cara ini yaitu bersarang dari dalam blok yang dikirim ke antrian latar belakang.

  • pemrosesan latar belakang selesai -> perbarui UI
  • potongan data diproses pada antrian latar belakang -> sinyal antrian utama untuk memulai potongan berikutnya
  • data jaringan yang masuk pada antrian latar belakang -> sinyal antrian utama bahwa pesan telah tiba
  • dll

Mengenai mengapa Anda mungkin ingin mengirim ke antrian utama dari antrian utama ... Yah, Anda biasanya tidak akan berpikir bahwa Anda mungkin melakukannya untuk menjadwalkan beberapa pekerjaan yang akan dilakukan di putaran run loop berikutnya.

Robin Summerhill
sumber
Ah, begitu. Jadi saya benar. Tidak ada keuntungan melakukan hal itu jika Anda sudah berada di antrian utama, hanya jika Anda berada di antrian lain dan ingin memperbarui UI. Terima kasih.
Bebek
Cukup edit jawaban saya untuk membicarakan mengapa tidak berguna untuk melakukan ini dari antrian utama.
Robin Summerhill
Juga, saya pikir ada bug di iOS 4 (mungkin sudah ada di iOS 5), di mana dispatch_sync ke antrian utama dari utas utama hanya menyebabkan hang, jadi saya akan menghindari melakukan itu sepenuhnya.
joerick
10
Itu bukan bug, itu perilaku yang diharapkan. Memang perilaku ini tidak terlalu berguna tetapi Anda harus selalu menyadari kebuntuan saat menggunakan dispatch_sync. Anda tidak dapat mengharapkan sistem melindungi Anda dari kesalahan pemrogram sepanjang waktu.
Robin Summerhill
2
Apa itu backgroundQueue di sini? Bagaimana cara membuat objek backgroundQueue
Nilesh Tupe
16

Mengirim blok ke antrian utama dari can utama bermanfaat. Ini memberi antrian utama peluang untuk menangani blok lain yang telah di-antri sehingga Anda tidak hanya memblokir segala sesuatu yang lain dari eksekusi.

Misalnya, Anda dapat menulis server ulir tunggal yang pada dasarnya menangani banyak koneksi bersamaan. Selama tidak ada blok individual dalam antrian yang terlalu lama server tetap responsif terhadap permintaan baru.

Jika program Anda tidak melakukan apa pun kecuali menghabiskan seluruh hidupnya untuk merespons berbagai peristiwa, maka ini bisa sangat wajar. Anda cukup mengatur handler acara Anda untuk berjalan di antrian utama dan kemudian memanggil dispatch_main (), dan Anda mungkin tidak perlu khawatir tentang keamanan utas sama sekali.

bames53
sumber
11

Semoga saya memahami pertanyaan Anda dengan benar karena Anda bertanya-tanya tentang perbedaan antara dispatch_async dan dispatch_sync?

dispatch_async

akan mengirimkan blok ke antrian secara tidak sinkron. Berarti itu akan mengirim blok ke antrian dan tidak menunggu untuk kembali sebelum melanjutkan eksekusi kode yang tersisa dalam metode Anda.

dispatch_sync

akan mengirim blok ke antrian secara serempak. Ini akan mencegah eksekusi kode yang tersisa dalam metode sampai blok selesai dieksekusi.

Saya sebagian besar menggunakan dispatch_asyncantrian latar belakang untuk mulai bekerja dari antrian utama dan memanfaatkan setiap core tambahan yang mungkin dimiliki perangkat. Kemudiandispatch_async ke utas utama jika saya perlu memperbarui UI.

Semoga berhasil

timthetoolman
sumber
1
terima kasih, tetapi saya bertanya tentang keuntungan dari mengirim sesuatu ke antrian utama, berada di antrian utama.
Bebek
9

Satu tempat yang bermanfaat adalah untuk aktivitas UI, seperti mengatur pemintal sebelum operasi yang panjang:

- (void) handleDoSomethingButton{

    [mySpinner startAnimating];

    (do something lengthy)
    [mySpinner stopAnimating];
}

tidak akan berfungsi, karena Anda memblokir utas selama hal panjang Anda dan tidak membiarkan UIKit benar-benar memulai pemintal.

- (void) handleDoSomethingButton{
     [mySpinner startAnimating];

     dispatch_async (dispatch_get_main_queue(), ^{
          (do something lengthy)
          [mySpinner stopAnimating];
    });
}

akan mengembalikan kontrol ke run loop, yang akan menjadwalkan pembaruan UI, memulai pemintal, kemudian akan mendapatkan hal berikutnya dari antrian pengiriman, yang merupakan pemrosesan aktual Anda. Ketika pemrosesan Anda selesai, animasi berhenti dipanggil, dan Anda kembali ke loop dijalankan, di mana UI kemudian diperbarui dengan berhenti.

weaselfloss1
sumber
@ Jerceratops ya tapi itu memungkinkan runloop saat ini untuk menyelesaikan.
Dan Rosenstark
3
Ya, tapi masih mengerikan. Itu masih memblokir UI. Saya mungkin menekan tombol lain setelah ini. Atau coba dan gulir. "(melakukan sesuatu yang panjang)" seharusnya tidak terjadi pada utas utama, dan dispatch_async untuk membiarkan tombol klik "selesai" bukanlah solusi yang dapat diterima.
Jerceratops
8

Swift 3, 4 & 5

Menjalankan kode di utas utama

DispatchQueue.main.async {
    // Your code here
}
Niall Kiddle
sumber
1

Async berarti asinkron dan Anda harus sering menggunakannya. Anda seharusnya tidak pernah memanggil sinkronisasi pada utas utama karena itu akan mengunci UI Anda sampai tugas selesai. Anda di sini adalah cara yang lebih baik untuk melakukan ini di Swift:

runThisInMainThread { () -> Void in
    // Run your code like this:
    self.doStuff()
}

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