Gunakan kumpulan tugas Swift untuk mengumpulkan pengiraan yang bersaing mengikut turutan siap, abaikan nil, dan batalkan anak yang selebihnya apabila suatu nilai tiba. Contoh baris perintah Mac ini mencari susun atur segi empat yang boleh digunakan untuk jubin sintetik dan menerangkan bila hasilnya benar-benar boleh dipulangkan.

Tentukan maksud hasil berguna pertama

Katakan alat susun atur desktop kecil perlu menyusun 221 jubin sintetik menjadi segi empat. Beberapa pengiraan setempat mencari bilangan baris yang berbeza. Sebarang faktor bukan remeh boleh diterima: 13 baris dan 17 lajur boleh digunakan, begitu juga 17 baris dan 13 lajur. Tugas pembaca ialah menerima mana-mana pengiraan sah yang selesai dahulu.

Ini ialah kontrak mengikut turutan siap. Kontrak ini tidak memilih faktor terkecil, segi empat yang paling seimbang atau pengiraan yang disenaraikan dahulu. Apple mendokumenkan bahawa hasil kumpulan tugas berturut-turut tiba mengikut turutan siap, tanpa mengira turutan sisipan. TaskGroup.next(isolation:)

Tentukan tingkah laku sebelum menulis pembantu:

Situasi Tingkah laku yang diperlukan
Pengiraan selesai dengan nil Terus menunggu pengiraan lain.
Pengiraan selesai dengan suatu nilai Pilih nilai itu dan minta pembatalan kerja yang selebihnya.
Semua pengiraan memulangkan nil Pulangkan nil.
Senarai pengiraan kosong Pulangkan nil.
Pembatalan induk dikesan Berhenti memilih hasil dan pulangkan nil.

Baris terakhir ialah pilihan API yang disengajakan untuk contoh ini. Di sini, nil bermaksud seruan itu tidak membekalkan nilai yang boleh digunakan; ia tidak membezakan carian yang gagal daripada pembatalan. Jika pemanggil anda memerlukan perbezaan itu, gunakan jenis hasil yang jelas atau kontrak yang melemparkan ralat.

Bina contoh setempat yang lengkap pada Mac anda

Gunakan rantaian alat Swift 6 atau lebih baharu. Di Terminal, semak dengan swift --version. Jika Swift tidak tersedia, ikuti arahan rasmi Install Swift — macOS. Program ini menggunakan Foundation dan API keserentakan Swift tanpa kebergantungan pakej luaran.

Cipta pakej boleh laksana baharu dengan arahan ini. Ini mengikut aliran kerja penciptaan dan pelaksanaan pakej yang didokumenkan oleh Swift.org. Build a Command-line Tool

mkdir FirstNonNilDemo
cd FirstNonNilDemo
swift package init --name FirstNonNilDemo --type executable

Gantikan kod sumber boleh laksana yang dijana dengan program lengkap di bawah. Bergantung pada susun atur yang dijana, namanya mungkin Sources/FirstNonNilDemo/FirstNonNilDemo.swift atau Sources/main.swift. Gunakan satu fail titik masuk bernama FirstNonNilDemo.swift; jika templat itu mencipta main.swift, namakan semula fail itu sebelum menambah program @main. Panduan Swift mendokumenkan bahawa program menggunakan sama ada main.swift atau titik masuk @main. Build a Command-line Tool

import Foundation

func firstNonNil<Value: Sendable>(
    _ calculations: [@Sendable () async -> Value?]
) async -> Value? {
    guard !Task.isCancelled else { return nil }

    return await withTaskGroup(
        of: Value?.self,
        returning: Value?.self
    ) { group in
        defer { group.cancelAll() }

        for calculation in calculations {
            let added = group.addTaskUnlessCancelled {
                guard !Task.isCancelled else { return nil }
                return await calculation()
            }
            guard added else { break }
        }

        for await candidate in group {
            guard !Task.isCancelled else { return nil }
            if let value = candidate {
                return value
            }
        }

        return nil
    }
}

func findFactor(total: Int, range: ClosedRange<Int>) -> Int? {
    guard total > 1 else { return nil }

    for rows in range {
        guard !Task.isCancelled else { return nil }
        guard rows > 1, rows < total else { continue }

        if total % rows == 0 {
            return rows
        }
    }

    return nil
}

@main
struct FirstNonNilDemo {
    static func main() async {
        let checks: [@Sendable () async -> Int?] = [
            { nil },
            { 42 }
        ]
        let checkResult = await firstNonNil(checks)
        precondition(checkResult == 42)

        let empty: [@Sendable () async -> Int?] = []
        let emptyResult = await firstNonNil(empty)
        precondition(emptyResult == nil)

        for rawText in [" 221\n", " 223 ", "tiles"] {
            let text = rawText.trimmingCharacters(
                in: .whitespacesAndNewlines
            )

            guard let total = Int(text), total > 1 else {
                print("Input must be a whole number greater than 1.")
                continue
            }

            let calculations: [@Sendable () async -> Int?] = [
                { findFactor(total: total, range: 2...5) },
                { findFactor(total: total, range: 6...16) },
                { findFactor(total: total, range: 17...30) }
            ]

            if let rows = await firstNonNil(calculations) {
                print("\(total) tiles: \(rows) rows x \(total / rows) columns")
            } else {
                print("\(total) tiles: no factor in the supplied ranges.")
            }
        }
    }
}

Jalankan pakej dari direktorinya:

swift run FirstNonNilDemo

Untuk 221, sama ada 221 tiles: 13 rows x 17 columns atau 221 tiles: 17 rows x 13 columns boleh diterima. Untuk 223, program melaporkan tiada faktor dalam julat yang diberikan. Teks tiles menghasilkan mesej input. Ini ialah hasil yang dijangka, bukannya transkrip pelaksanaan yang direkodkan. Aritmetik disemak secara berasingan; penyusun Swift tidak tersedia dalam persekitaran penyelidikan, jadi program Swift itu diperiksa tetapi tidak dilaksanakan.

Foundation hanya mengendalikan pembersihan ruang kosong di sekeliling. Kaedah trimmingCharacters(in:) membuang aksara yang sepadan dari kedua-dua hujung; kaedah itu tidak membuang aksara di dalam rentetan. Oleh itu, 221 menjadi 221, manakala 2 21 kekal tidak sah untuk program ini. trimmingCharacters(in:)

Sebab pembantu mengabaikan nil tanpa kehilangan hasil kemudian

Jenis hasil anak kumpulan ialah Value?. Oleh itu, setiap anak membekalkan sama ada nilai yang boleh digunakan atau hasil kosong biasa. Gelung for await menerima hasil anak yang telah selesai, dan pengikatan pilihan di dalamnya memilih hanya calon bukan nil.

Terdapat dua jenis kekosongan yang berbeza jika anda menggunakan group.next() secara langsung. Jenis pulangan yang didokumenkan ialah ChildTaskResult?. Dengan hasil anak pilihan, jenis itu menjadi Value??: pilihan luar mewakili sama ada hasil anak seterusnya wujud, manakala pilihan dalam mewakili sama ada anak itu menemukan nilai. TaskGroup.next(isolation:)

Perbezaan ini menerangkan kesilapan lazim: mengambil satu hasil lalu terus memulangkannya. Jika pengiraan yang selesai dahulu memulangkan nil, anak lain mungkin masih mempunyai jawapan yang berguna. Gelung dalam contoh lengkap terus menggunakan hasil sehingga menemukan nilai atau kumpulan itu kehabisan hasil.

Pembantu menggunakan addTaskUnlessCancelled dan menyemak nilai pulangan Boolnya. Apple mendokumenkan bahawa kaedah ini tidak mencipta anak jika kumpulan sudah dibatalkan. Memutuskan gelung penghantaran mengelakkan penghantaran kerja yang berterusan selepas keadaan itu dikesan. addTaskUnlessCancelled(priority:operation:)

Pengiraan menangkap bilangan jubin yang tidak berubah dan memulangkan jawapannya melalui kumpulan. Pengiraan itu tidak mengemas kini pemboleh ubah pemenang yang dikongsi. Hasil generik mesti mematuhi Sendable, selaras dengan kekangan kumpulan tugas yang didokumenkan. withTaskGroup(of:returning:isolation:body:)

Sebab memilih pemenang tidak bermakna terus memulangkan hasil

Memulangkan nilai daripada penutupan kumpulan memilih jawapan, tetapi withTaskGroup yang melingkunginya masih menunggu semua anak selesai. Apple secara jelas mendokumenkan bahawa anak yang dibatalkan sekalipun mesti selesai sebelum fungsi memulangkan hasil. withTaskGroup(of:returning:isolation:body:)

Blok defer memanggil group.cancelAll() setiap kali penutupan keluar. Ini meminta pembatalan pengiraan yang masih berjalan. Pembatalan tidak menghentikan pengiraan sewenang-wenangnya: anak mesti bekerjasama dan selesai lebih awal dengan sendiri. TaskGroup

Dalam contoh ini, findFactor menyemak Task.isCancelled sebelum setiap pembahagi calon. Setiap unit kerja kecil, jadi peluang untuk berhenti kerap tersedia. Untuk pengiraan yang lebih besar, letakkan semakan di antara bahagian kerja yang bermakna dan terhad. Semakan hanya sebelum operasi panjang yang tidak boleh dibahagikan membiarkan operasi itu berjalan sehingga selesai. Panduan bahasa Swift menerangkan bahawa pembatalan bersifat kerjasama dan menyatakan bahawa memulangkan nil ialah salah satu tindak balas yang disokong. Concurrency

Jika pengiraan yang bersaing tidak pernah selesai dan tidak pernah bertindak balas terhadap pembatalan, pembantu berstruktur ini tidak dapat menjamin pemulangan yang segera. Cara yang disokong ialah memastikan setiap pekerja mempunyai had masa atau peka terhadap pembatalan. Menambah hasil tamat masa yang bersaing tidak menghapuskan kewajipan kumpulan untuk menunggu anak-anaknya.

Pembatalan induk disemak sebelum penghantaran, semasa anak bermula, di dalam gelung aritmetik dan sebelum menerima setiap hasil. Semakan ini mengesan pembatalan pada saat tertentu; semakan ini tidak mewujudkan peraturan atomik yang memastikan pembatalan sentiasa mengatasi nilai. Pembatalan yang tiba selepas semakan terakhir mungkin berlaku serentak dengan pemulangan yang berjaya.

Pilih peraturan pemilihan yang betul dan sahkan sempadannya

Contoh sintetik ini menyediakan ujian keputusan yang berguna: 13 dan 17 kedua-duanya memenuhi kontrak yang dinyatakan. Jika alat anda memerlukan bilangan baris yang lebih kecil, menerima mana-mana hasil adalah tidak betul. Turutan siap hanya berguna apabila setiap jawapan yang diterima boleh saling menggantikan untuk tugas pembaca.

Keperluan pemanggil Pendekatan yang sesuai
Sebarang nilai yang boleh diterima sebaik sahaja pengiraan selesai Gunakan gelung kumpulan tugas yang ditunjukkan di atas.
Pengiraan berjaya yang paling awal dalam senarai keutamaan tetap Nilai mengikut turutan keutamaan, atau simpan indeks dan selesaikan calon yang lebih awal sebelum memilih.
Hasil terkecil atau dengan skor terbaik Bandingkan hasil yang berkaitan sebelum memilih.
Beberapa hasil yang diketahui diperlukan bersama Pertimbangkan async let.

Panduan bahasa Swift memperkenalkan async let untuk tugas anak yang diketahui dan hasilnya diperlukan kemudian, serta kumpulan tugas untuk kawalan jelas terhadap anak yang ditambah secara dinamik. Menunggu hasil bernama mengikut turutan yang dipilih tidak melaksanakan gelung pemilihan mengikut turutan siap. Concurrency

Gunakan semakan ini apabila menyesuaikan program:

  • Kekalkan penegasan senarai kosong: ia sepatutnya memulangkan nil.
  • Kekalkan penegasan campuran nil dan 42: ia sepatutnya memulangkan 42 tanpa mengira turutan siap.
  • Uji pengiraan yang semuanya memulangkan nil.
  • Untuk berbilang jawapan yang sah, tegaskan bahawa hasil termasuk dalam set sah dan bukannya pemenang tertentu.
  • Periksa setiap pekerja untuk memastikan kemajuannya terhad dan mempunyai semakan pembatalan.
  • Laporkan carian yang tidak berjaya sebagai terhad kepada julat yang diberikan; jangan membuat kesimpulan bahawa tiada jawapan wujud di tempat lain.

Tiga imbasan faktor kecil ini menunjukkan tingkah laku, bukan peningkatan kelajuan. Imbasan berjujukan memadai untuk nombor ilustrasi ini. Gunakan corak pengiraan bersaing apabila tugas sebenar mendapat manfaat daripada menerima hasil yang boleh saling menggantikan, dan ukur beban kerja itu sebelum menentukan jumlah kerja yang akan dijalankan serentak.

Pastikan peraturan penerimaan jelas, abaikan hasil anak yang kosong dan pastikan kerja yang selebihnya boleh dihentikan. Ini memberikan kontrak nilai yang boleh dijangka kepada alat Mac: hasil boleh guna pertama yang dihantar oleh kumpulan, diikuti penyelesaian proses pemberhentian anak-anak secara kerjasama.

Sumber