Memahami dispatch_async

233

Saya memiliki pertanyaan seputar kode ini

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData* data = [NSData dataWithContentsOfURL: 
      kLatestKivaLoansURL];
    [self performSelectorOnMainThread:@selector(fetchedData:) 
      withObject:data waitUntilDone:YES];
});

Parameter pertama dari kode ini adalah

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 

Apakah kita meminta kode ini untuk melakukan tugas serial pada antrian global yang definisinya sendiri adalah mengembalikan antrian serentak global dari tingkat prioritas yang diberikan?

Apa keuntungan menggunakan dispatch_get_global_queuelebih dari antrian utama?

Saya bingung. Bisakah Anda membantu saya untuk memahami ini dengan lebih baik.

pengguna2332873
sumber
1
Anda sebaiknya memotong kode Anda dalam beberapa baris sehingga lebih masuk akal. amankan dispatch_get_global_queuedi dalam Anda tipe variabel dispatch_queue_t myQueue. Lebih mudah dibaca hanya melewati myQueue ke `` dispatch_async` Anda
Alex Cio

Jawaban:

517

Alasan utama Anda menggunakan antrian default di atas antrian utama adalah untuk menjalankan tugas di latar belakang.

Misalnya, jika saya mengunduh file dari internet dan saya ingin memperbarui pengguna pada kemajuan unduhan, saya akan menjalankan unduhan dalam antrian standar prioritas dan memperbarui UI dalam antrian utama secara tidak sinkron.

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    //Background Thread
    dispatch_async(dispatch_get_main_queue(), ^(void){
        //Run UI Updates
    });
});
David
sumber
Saya mengerti bahwa terima kasih terima kasih atas jawaban Anda itu, tetapi pertanyaan saya lebih lanjut untuk memahami logika melakukan hal ini yaitu meminta kode ini untuk melakukan tugas-tugas serial pada antrian global yang merupakan antrian bersamaan sendiri
user2332873
Saya melakukan persis apa yang Anda sarankan tetapi entah bagaimana, uiTableViewCell tidak memperbarui segera ketika saya memanggil [self.tableView reloadData] di Run UI Updates. Butuh sekitar 4 atau 5 detik. Ini membuat saya gila selama beberapa hari sekarang. .
GrandSteph
@ GrandSteph Saya tidak terlalu terbiasa dengan metode itu. Mungkin metode itu hanya membutuhkan waktu 5 detik untuk berjalan. Yang penting dengan dispatch_async adalah memungkinkan Anda melakukan hal-hal di latar belakang tanpa menggantung utas utama.
David
2
apa artinya 0?
Sayang
3
@ Madu 0 adalah flagsparameter, yang saat ini tidak melakukan apa pun. Dari dokumen:Flags that are reserved for future use. Always specify 0 for this parameter.
David
199

Semua antrian DISPATCH_QUEUE_PRIORITY_X adalah antrian bersamaan (artinya mereka dapat menjalankan banyak tugas sekaligus), dan FIFO dalam arti bahwa tugas-tugas dalam antrian yang diberikan akan mulai dieksekusi menggunakan urutan "masuk pertama, keluar pertama". Ini dibandingkan dengan antrian utama (dari dispatch_get_main_queue ()), yang merupakan antrian serial (tugas akan mulai dieksekusi dan selesai dieksekusi dalam urutan penerimaannya).

Jadi, jika Anda mengirim 1000 blok dispatch_async () ke DISPATCH_QUEUE_PRIORITY_DEFAULT, tugas-tugas itu akan mulai dieksekusi dalam urutan yang Anda kirim ke antrian. Demikian juga untuk antrian TINGGI, RENDAH, dan LATAR BELAKANG. Apa pun yang Anda kirim ke salah satu antrian ini dijalankan di latar belakang pada utas alternatif, jauh dari utas aplikasi utama Anda. Oleh karena itu, antrian ini cocok untuk menjalankan tugas-tugas seperti pengunduhan latar belakang, kompresi, perhitungan, dll.

Perhatikan bahwa urutan eksekusi adalah FIFO berdasarkan per-antrian. Jadi jika Anda mengirim 1000 dispatch_async () tugas ke empat antrian serentak yang berbeda, membaginya secara merata dan mengirimkannya ke BACKGROUND, LOW, DEFAULT, dan HIGH agar (yaitu Anda menjadwalkan 250 tugas terakhir pada antrian TINGGI), sangat mungkin bahwa tugas pertama yang Anda lihat akan dimulai pada antrian TINGGI karena sistem telah mengambil implikasi Anda bahwa tugas-tugas tersebut harus sampai ke CPU secepat mungkin.

Perhatikan juga bahwa saya mengatakan "akan mulai mengeksekusi secara berurutan", tetapi perlu diingat bahwa sebagai antrian bersamaan, hal-hal tidak harus SELESAI dieksekusi dalam urutan tergantung pada lamanya waktu untuk setiap tugas.

Sesuai Apple:

https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

Antrian pengiriman bersamaan berguna ketika Anda memiliki banyak tugas yang dapat berjalan secara paralel. Antrian konkuren masih merupakan antrian karena ia membagi tugas dengan urutan masuk pertama, keluar pertama; namun, antrian konkuren dapat memberikan tugas tambahan sebelum tugas sebelumnya selesai. Jumlah aktual tugas yang dijalankan oleh antrian konkuren pada saat tertentu adalah variabel dan dapat berubah secara dinamis karena kondisi dalam aplikasi Anda berubah. Banyak faktor yang mempengaruhi jumlah tugas yang dijalankan oleh antrian bersamaan, termasuk jumlah core yang tersedia, jumlah pekerjaan yang dilakukan oleh proses lain, dan jumlah dan prioritas tugas dalam antrian pengiriman serial lainnya.

Pada dasarnya, jika Anda mengirim 1000 blok dispatch_async () ke antrian DEFAULT, HIGH, LOW, atau BACKGROUND, mereka semua akan mulai mengeksekusi dalam urutan yang Anda kirim. Namun, tugas yang lebih pendek dapat diselesaikan sebelum yang lebih lama. Alasan di balik ini adalah jika ada core CPU yang tersedia atau jika tugas antrian saat ini melakukan pekerjaan komputasi non-intensif (sehingga membuat sistem berpikir itu dapat mengirimkan tugas tambahan secara paralel terlepas dari jumlah inti).

Tingkat konkurensi ditangani sepenuhnya oleh sistem dan didasarkan pada beban sistem dan faktor-faktor lain yang ditentukan secara internal. Ini adalah keindahan Grand Central Dispatch (sistem dispatch_async ()) - Anda cukup menjadikan unit kerja Anda sebagai blok kode, menetapkan prioritas untuk mereka (berdasarkan antrian yang Anda pilih) dan membiarkan sistem menangani sisanya.

Jadi untuk menjawab pertanyaan Anda di atas: Anda sebagian benar. Anda "meminta kode itu" untuk melakukan tugas serentak pada antrian serentak global pada tingkat prioritas yang ditentukan. Kode dalam blok akan dieksekusi di latar belakang dan kode tambahan (serupa) apa pun akan dieksekusi secara potensial paralel tergantung pada penilaian sistem terhadap sumber daya yang tersedia.

Antrian "utama" di sisi lain (dari dispatch_get_main_queue ()) adalah antrian serial (tidak bersamaan). Tugas yang dikirim ke antrian utama akan selalu dijalankan secara berurutan dan akan selalu selesai. Tugas-tugas ini juga akan dieksekusi pada UI Thread sehingga cocok untuk memperbarui UI Anda dengan pesan progres, pemberitahuan penyelesaian, dll.

SimplePanda
sumber
+1, tapi saya pikir dalam praktiknya tidak masalah apakah antrian bersamaan adalah FIFO atau hanya pesanan acak. Jika Anda memulai 5 tugas dalam satu lingkaran, asumsikan bahwa mereka pada dasarnya akan mulai pada saat yang sama. Tidak ada jaminan bahwa mis. Operasi I / O pertama dari tugas pertama akan terjadi sebelum tanggal 5, bahkan jika mereka menjalankan kode yang sama. OTOH, untuk antrian serial, perilaku FIFO sangat penting dan IMHO ini adalah perbedaan yang menentukan antara kedua jenis antrian.
Gerhard Wesp
Penjelasan luar biasa. Tepuk tangan banyak!
Okhan Okbay
36

Versi cepat

Ini adalah versi Swift dari jawaban Objective-C David. Anda menggunakan antrian global untuk menjalankan hal-hal di latar belakang dan antrian utama untuk memperbarui UI.

DispatchQueue.global(qos: .background).async {
    
    // Background Thread
    
    DispatchQueue.main.async {
        // Run UI Updates
    }
}
Suragch
sumber