Saat menggunakan GCD, kami ingin menunggu sampai dua blok async dieksekusi dan dilakukan sebelum beralih ke langkah eksekusi selanjutnya. Apa cara terbaik untuk melakukannya?
Kami mencoba yang berikut, tetapi sepertinya tidak berhasil:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block1
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block2
});
// wait until both the block1 and block2 are done before start block3
// how to do that?
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
// block3
});
Jawaban:
Gunakan grup pengiriman: lihat di sini untuk contoh, "Menunggu di Grup dari Tugas yang Di-antri" di bab "Pengiriman Antrian" di Panduan Pemrograman Concurrency Perpustakaan Pengembang Apple Apple.
Contoh Anda dapat terlihat seperti ini:
dan dapat menghasilkan output seperti ini:
sumber
dispatch_group_async
sama sepertidispatch_async
dengan parameter grup ditambahkan. Jadi jika Anda menggunakan antrian yang berbeda untuk block1 dan block2 atau menjadwalkannya pada antrian bersamaan yang sama, mereka dapat berjalan secara bersamaan; jika Anda menjadwalkannya pada antrian seri yang sama, mereka akan berjalan secara seri. Tidak ada bedanya dengan menjadwalkan blok tanpa grup.Memperluas jawaban Jörn Eyrich (membatalkan jawaban jika Anda membatalkan jawaban ini), jika Anda tidak memiliki kendali atas
dispatch_async
panggilan untuk blok Anda, seperti halnya untuk blok penyelesaian async, Anda dapat menggunakan grup GCD menggunakandispatch_group_enter
dandispatch_group_leave
secara langsung.Dalam contoh ini, kita berpura-pura
computeInBackground
adalah sesuatu yang tidak dapat kita ubah (bayangkan itu adalah panggilan balik delegasi, Penanganan sambungan NSURLConnection, atau apa pun), dan dengan demikian kita tidak memiliki akses ke panggilan pengiriman.Dalam contoh ini, computeInBackground: completion: diimplementasikan sebagai:
Output (dengan cap waktu dari lari):
sumber
dispatch_queue_notify
kemungkinan lebih baik (kecuali waktu pemblokiran dijamin pendek).Dengan Swift 5.1, Grand Central Dispatch menawarkan banyak cara untuk menyelesaikan masalah Anda. Sesuai dengan kebutuhan Anda, Anda dapat memilih salah satu dari tujuh pola yang ditunjukkan dalam cuplikan Playground berikut.
# 1. Menggunakan
DispatchGroup
,DispatchGroup
'snotify(qos:flags:queue:execute:)
danDispatchQueue
' sasync(group:qos:flags:execute:)
Panduan Pemrograman Concurrency Pengembang Apple menyatakan tentang
DispatchGroup
:# 2. Menggunakan
DispatchGroup
,DispatchGroup
'swait()
,DispatchGroup
'enter()
danDispatchGroup
'leave()
Perhatikan bahwa Anda juga dapat bergaul
DispatchGroup
wait()
denganDispatchQueue
async(group:qos:flags:execute:)
atau menggabungkanDispatchGroup
enter()
danDispatchGroup
leave()
denganDispatchGroup
notify(qos:flags:queue:execute:)
.# 3. Menggunakan dan 's
DispatchWorkItemFlags
barrier
DispatchQueue
async(group:qos:flags:execute:)
Tutorial Grand Central Dispatch untuk Swift 4: Bagian 1/2 artikel dari Raywenderlich.com memberikan definisi untuk hambatan :
Pemakaian:
# 4 Menggunakan
DispatchWorkItem
,DispatchWorkItemFlags
'sbarrier
danDispatchQueue
' sasync(execute:)
# 5 Menggunakan
DispatchSemaphore
,DispatchSemaphore
'swait()
danDispatchSemaphore
' ssignal()
Soroush Khanlou menulis baris berikut dalam posting blog The GCD Handbook :
Referensi API Pengembang Apple juga memberikan diskusi berikut untuk
DispatchSemaphore
init(value:)
penginisialisasi:Pemakaian:
# 6. Menggunakan
OperationQueue
danOperation
'saddDependency(_:)
Referensi API Pengembang Apple menyatakan tentang
OperationQueue
:Pemakaian:
# 7. Menggunakan
OperationQueue
danOperationQueue
'saddBarrierBlock(_:)
(membutuhkan iOS 13)sumber
Alternatif GCD lainnya adalah penghalang:
Cukup buat antrian bersamaan, kirim dua blok Anda, dan kirim blok terakhir dengan penghalang, yang akan membuatnya menunggu dua lainnya selesai.
sumber
sleep()
! Saya hanya menambahkansleep()
panggilan itu untuk alasan pedagogis, untuk membuat blok berjalan cukup lama sehingga Anda dapat melihat bahwa mereka berjalan secara bersamaan. Dalam contoh sepele ini, jika tidak adasleep()
, dua blok ini dapat berjalan sangat cepat sehingga blok yang dikirim mungkin mulai dan selesai sebelum Anda memiliki kesempatan untuk secara empiris mengamati eksekusi bersamaan. Tapi jangansleep()
di kode Anda sendiri.Saya tahu Anda bertanya tentang GCD, tetapi jika Anda mau,
NSOperationQueue
juga menangani hal-hal semacam ini dengan sangat anggun, misalnya:sumber
NSOperation
subkelas yang bersamaan dan diaturisFinished
ketika proses asinkron selesai. Kemudian dependensi berfungsi dengan baik.dispatch_semaphore_wait
tidak terjadi pada antrian utama dan selama sinyal dan menunggu Anda seimbang). Selama Anda tidak memblokir antrian utama, pendekatan semaphore baik-baik saja, jika Anda tidak memerlukan fleksibilitas operasi (misalnya memiliki kemampuan untuk membatalkannya, kemampuan untuk mengontrol tingkat konkurensi, dll).maxConcurrentOperationCount
ke1
. Anda dapat menetapkan prioritas operasi, juga,qualityOfService
danqueuePriority
, tetapi ini memiliki dampak yang jauh lebih halus pada prioritas tugas daripada dependensi dan / atau tingkat konkurensi antrian.Jawaban di atas semua keren, tetapi mereka semua melewatkan satu hal. grup menjalankan tugas (blok) di utas tempat masuknya ketika Anda menggunakan
dispatch_group_enter
/dispatch_group_leave
.ini berjalan dalam antrian bersamaan yang dibuat
demoQueue
. Jika saya tidak membuat antrian, itu berjalan di utas utama .dan ada cara ketiga untuk membuat tugas dijalankan di utas lain:
Tentu saja, seperti yang disebutkan dapat Anda gunakan
dispatch_group_async
untuk mendapatkan apa yang Anda inginkan.sumber
Jawaban pertama pada dasarnya benar, tetapi jika Anda ingin cara paling sederhana untuk mencapai hasil yang diinginkan, berikut ini adalah contoh kode yang berdiri sendiri yang menunjukkan cara melakukannya dengan semaphore (yang juga merupakan cara pengiriman grup bekerja di belakang layar, JFYI) :
sumber
dispatch_semaphore_wait
. Anda memiliki dua sinyal, jadi Anda perlu dua menunggu. Seperti, blok "selesai" Anda akan mulai segera setelah blok pertama memberi sinyal pada semaphore, tetapi sebelum blok lainnya selesai; 2. Mengingat ini adalah pertanyaan iOS, saya tidak akan menyarankan penggunaandispatch_main
.dispatch_semaphore_wait
akan membuka blokir sesegera salah satudispatch_semaphore_signal
metode yang disebut. Alasan mengapa hal ini tampaknya berhasil adalahprintf
karena blok 'satu' dan 'dua' terjadi segera, danprintf
untuk 'akhirnya' terjadi setelah menunggu - dengan demikian setelah blok satu telah tidur selama 2 detik. Jika Anda meletakkan printf setelahsleep
panggilan, Anda akan mendapatkan output untuk 'satu', lalu 2 detik kemudian untuk 'akhirnya', lalu 2 detik kemudian untuk 'dua'.Jawaban yang diterima dengan cepat:
sumber
Contoh Swift 4.2:
sumber
group.leave()
menyebabkan crashBukan untuk mengatakan jawaban lain tidak bagus untuk keadaan tertentu, tetapi ini adalah satu cuplikan saya selalu pengguna dari Google:
sumber