Tamamlanma sırasına göre yarışan hesaplamaları toplamak, nil değerini atlamak ve bir değer geldiğinde kalan alt görevleri iptal etmek için Swift görev grubu kullanın. Bu Mac komut satırı örneği, sentetik döşemeler için kullanılabilir bir dikdörtgen yerleşimi bulur ve sonucun gerçekten ne zaman dönebileceğini açıklar.

İlk yararlı sonucun ne sayıldığını tanımlayın

Küçük bir masaüstü yerleşim aracının 221 sentetik döşemeyi bir dikdörtgen halinde düzenlemesi gerektiğini varsayalım. Birkaç yerel hesaplama, olası farklı satır sayılarını arar. Sıfırdan farklı herhangi bir çarpan kabul edilebilir: 13 satır ve 17 sütun da 17 satır ve 13 sütun da işe yarar. Okuyucunun görevi, geçerli hesaplamalardan hangisi önce tamamlanırsa onu kabul etmektir.

Bu, tamamlanma sırasına dayalı bir sözleşmedir. En küçük çarpanı, en dengeli dikdörtgeni veya listede ilk sırada yer alan hesaplamayı seçmez. Apple, görev grubu sonuçlarının ekleme sırasından bağımsız olarak tamamlanma sırasıyla geldiğini belgeliyor. TaskGroup.next(isolation:)

Yardımcı işlevi yazmadan önce davranışı tanımlayın:

Durum Gerekli davranış
Bir hesaplama nil ile tamamlanır Diğer hesaplamaları beklemeye devam edin.
Bir hesaplama bir değerle tamamlanır Bu değeri seçin ve kalan işlerin iptal edilmesini isteyin.
Tüm hesaplamalar nil döndürür nil döndürün.
Hesaplama listesi boştur nil döndürün.
Üst görev iptali algılanır Sonuçları seçmeyi bırakın ve nil döndürün.

Son satır, bu örnek için bilinçli bir API tercihidir. Burada nil, çağrının kullanılabilir bir değer sağlamadığı anlamına gelir; başarısız bir aramayla iptal arasında ayrım yapmaz. Çağıran kodun bu ayrımı yapması gerekiyorsa açık bir sonuç türü veya hata fırlatan bir sözleşme kullanın.

Mac'inizde eksiksiz bir yerel örnek oluşturun

Swift 6 veya daha yeni bir araç zinciri kullanın. Terminal'de swift --version komutuyla denetleyin. Swift yüklü değilse resmi Install Swift — macOS yönergelerini izleyin. Program, harici paket bağımlılıkları olmadan Foundation ve Swift eşzamanlılık API'lerini kullanır.

Bu komutlarla yeni bir çalıştırılabilir paket oluşturun. Bu işlem, Swift.org'un belgelenmiş paket oluşturma ve çalıştırma iş akışını izler. Build a Command-line Tool

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

Oluşturulan çalıştırılabilir kaynak dosyasını aşağıdaki eksiksiz programla değiştirin. Oluşturulan yapıya bağlı olarak dosyanın adı Sources/FirstNonNilDemo/FirstNonNilDemo.swift veya Sources/main.swift olabilir. FirstNonNilDemo.swift adlı tek bir giriş noktası dosyası kullanın; şablon main.swift oluşturduysa @main programını eklemeden önce bu dosyanın adını değiştirin. Swift kılavuzu, bir programın main.swift veya @main giriş noktasını kullandığını belgeliyor. 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.")
            }
        }
    }
}

Paketi kendi dizininden çalıştırın:

swift run FirstNonNilDemo

221 için 221 tiles: 13 rows x 17 columns veya 221 tiles: 17 rows x 13 columns çıktılarından herhangi biri kabul edilebilir. 223 için program, verilen aralıklarda çarpan bulunamadığını bildirir. tiles metni, girdi mesajını üretir. Bunlar beklenen sonuçlardır; kaydedilmiş bir çalıştırma dökümü değildir. Aritmetik ayrıca denetlendi; araştırma ortamında Swift derleyicisi bulunmadığından Swift programı incelendi ancak çalıştırılmadı.

Foundation yalnızca çevredeki boşlukların temizlenmesini sağlar. trimmingCharacters(in:) yöntemi, eşleşen karakterleri dizenin her iki ucundan kaldırır; dizenin içindeki karakterleri kaldırmaz. Bu nedenle 221, 221 olurken 2 21 bu program için geçersiz kalır. trimmingCharacters(in:)

Yardımcı işlev nil değerlerini atlayıp sonraki sonuçları nasıl korur

Grubun alt görev sonuç türü Value? olur. Böylece her alt görev ya kullanılabilir bir değer ya da sıradan bir boş sonuç sağlar. for await döngüsü tamamlanan alt görev sonuçlarını alır; içindeki isteğe bağlı bağlama ise yalnızca nil olmayan bir adayı seçer.

group.next() değerini doğrudan kullanırsanız iki farklı türde boşluk vardır. Belgelenen dönüş türü ChildTaskResult? şeklindedir. Alt görev sonuçları isteğe bağlı olduğunda bu, Value?? olur: dıştaki isteğe bağlı değer başka bir alt görev sonucunun bulunup bulunmadığını, içteki isteğe bağlı değer ise o alt görevin bir değer bulup bulmadığını gösterir. TaskGroup.next(isolation:)

Bu ayrım yaygın bir hatayı açıklar: tek bir sonucu alıp hemen döndürmek. Tamamlanan ilk hesaplama nil döndürürse diğer alt görevlerde hâlâ yararlı yanıtlar olabilir. Eksiksiz örnekteki döngü, bir değer bulana veya grup tükenene kadar sonuçları almaya devam eder.

Yardımcı işlev addTaskUnlessCancelled kullanır ve Boolean dönüş değerini denetler. Apple, grup zaten iptal edilmişse bu yöntemin bir alt görev oluşturmadığını belgeliyor. Bu koşul algılandıktan sonra gönderim döngüsünü sonlandırmak, iş göndermeye devam edilmesini önler. addTaskUnlessCancelled(priority:operation:)

Hesaplamalar değişmez döşeme sayısını yakalar ve yanıtlarını grup üzerinden döndürür. Paylaşılan bir kazanan değişkenini güncellemezler. Genel sonuç türünün Sendable protokolüne uyması gerekir; bu, görev grubu için belgelenen kısıtlamayla örtüşür. withTaskGroup(of:returning:isolation:body:)

Kazananı seçmek neden hemen dönmek anlamına gelmez

Grup kapanışından bir değer döndürmek yanıtı seçer; ancak kapsayan withTaskGroup tüm alt görevlerin bitmesini bekler. Apple, iptal edilen alt görevlerin bile işlev dönmeden önce tamamlanması gerektiğini açıkça belgeliyor. withTaskGroup(of:returning:isolation:body:)

defer bloğu, kapanış her tamamlandığında group.cancelAll() çağrısını yapar. Bu, kalan hesaplamaların iptal edilmesini ister. İptal, keyfî bir hesaplamayı kesintiye uğratmaz: alt görevlerin buna uyum sağlaması ve erken bitmesi gerekir. TaskGroup

Bu örnekte findFactor, her aday bölen denetiminden önce Task.isCancelled değerini kontrol eder. Her iş birimi küçüktür, bu nedenle durmak için sık sık fırsat doğar. Daha büyük bir hesaplamada, anlamlı ve sınırlı parçaların arasına denetimler koyun. Uzun ve bölünemez bir işlemden önce yapılan tek bir denetim, işlem bitene kadar onun çalışmaya devam etmesine yol açar. Swift dil kılavuzu, iptalin iş birliğine dayalı olduğunu açıklar ve nil döndürmeyi desteklenen yanıtlardan biri olarak tanımlar. Concurrency

Yarışan bir hesaplama hiç bitmez ve iptale hiç yanıt vermezse bu yapılandırılmış yardımcı işlev hızlı dönüşü garanti edemez. Desteklenen yol, her çalışanı sınırlı çalışacak veya iptale duyarlı olacak şekilde tasarlamaktır. Yarışan bir zaman aşımı sonucu eklemek, grubun alt görevlerini bekleme yükümlülüğünü ortadan kaldırmaz.

Üst görev iptali gönderimden önce, alt görev girişinde, aritmetik döngüsünün içinde ve her sonucu kabul etmeden önce denetlenir. Bu denetimler iptali belirli anlarda gözlemler; iptalin her zaman bir değere üstün geldiği atomik bir kural oluşturmaz. Son denetimden sonra gelen bir iptal, başarılı dönüşle aynı ana denk gelebilir.

Doğru seçim kuralını belirleyin ve sınırlarını doğrulayın

Sentetik örnek yararlı bir karar testi sunar: hem 13 hem de 17 belirtilen sözleşmeyi karşılar. Aracınız daha küçük satır sayısını gerektiriyorsa herhangi bir sonucu kabul etmek yanlıştır. Tamamlanma sırasına göre seçim, yalnızca kabul edilen her yanıt okuyucunun görevi açısından birbirinin yerine geçebiliyorsa yararlıdır.

Çağıranın ihtiyacı Uygun yaklaşım
Hesaplamalar tamamlandıkça kabul edilebilir herhangi bir değer Yukarıda gösterilen görev grubu döngüsünü kullanın.
Sabit bir tercih listesindeki en erken başarılı hesaplama Tercih sırasına göre değerlendirin veya dizinleri saklayıp seçimden önce daha önceki adayları çözüme kavuşturun.
En küçük veya en yüksek puanlı sonuç Seçmeden önce ilgili sonuçları karşılaştırın.
Birlikte gereken, bilinen birkaç sonuç async let kullanmayı değerlendirin.

Swift dil kılavuzu, sonuçlarına daha sonra ihtiyaç duyulan bilinen alt görevler için async let kullanımını, dinamik olarak eklenen alt görevler üzerinde açık denetim içinse görev gruplarını sunar. Adlandırılmış sonuçları seçilen bir sırayla beklemek, tamamlanma sırasına dayalı bir seçim döngüsü uygulamaz. Concurrency

Programı uyarlarken şu denetimleri kullanın:

  • Boş liste doğrulamasını koruyun: nil döndürmelidir.
  • Karışık nil ve 42 doğrulamasını koruyun: tamamlanma sırasından bağımsız olarak 42 döndürmelidir.
  • Tümü nil döndüren hesaplamaları sınayın.
  • Birden fazla geçerli yanıt olduğunda belirli bir kazanan yerine geçerli kümede bulunmayı doğrulayın.
  • Her çalışanı, ilerlemesinin sınırlı olması ve iptal denetimleri açısından inceleyin.
  • Başarısız bir aramanın verilen aralıklarla sınırlı olduğunu belirtin; başka yerlerde yanıt bulunmadığı sonucunu çıkarmayın.

Üç küçük çarpan taraması hız artışını değil, davranışı gösterir. Bu örnek sayılar için sıralı tarama yeterlidir. Yarışan hesaplama düzenini, gerçek görev birbirinin yerine geçebilen yanıtları kabul etmekten yarar sağlıyorsa kullanın ve ne kadar işi eşzamanlı çalıştıracağınıza karar vermeden önce bu iş yükünü ölçün.

Kabul kuralını açıkça belirtin, boş alt görev sonuçlarını atlayın ve kalan işlerin durabilmesini sağlayın. Böylece Mac aracı öngörülebilir bir değer sözleşmesine sahip olur: grup tarafından iletilen ilk kullanılabilir sonuç ve ardından alt görevlerin iş birliğine dayalı kapanışının tamamlanması.

Kaynaklar