---
title: 'Eşzamanlılık Güvenliği'
source: 'https://academia.sh/tr/kurslar/swift/eszamanlilik-guvenligi'
course: 'Swift Dili'
language: tr
updated: '2026-10-21T05:03:12+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Aktör yalıtımının ne sağladığını ve await ile aktör sınırının nasıl geçildiğini açıklar'
    - 'Aktördeki lazy özelliğin birden çok eşzamanlı görevden okunsa da bir kez değerlendirildiğini ölçer'
    - 'Sendable ve @MainActor ile veri yarışının derlemede nasıl engellendiğini ayırt eder'
    - 'Sonucu hiç beklenmeyen async let bağlamasının yine değerlendirildiğini gösterir'
---

# Eşzamanlılık Güvenliği

Aktörün tembel özelliğinin eşzamanlı okumalarda bir kez kurulduğunu ve sonucu hiç beklenmeyen async let'in yine de değerlendirildiğini ölçer; Sendable ve @MainActor sınırlarını anlatır, kurs kapanışını yapar.

Önceki dersler tek akışta çalışan kodun değerlendirme anlarını ölçtü. Birden çok iş aynı anda aynı belleğe dokunduğunda başka bir soru doğar: bir değere aynı anda iki yerden yazılırsa ne olur. Buna **veri yarışı (data race)** denir ve sonucu tahmin edilemez. Swift 6 bu soruyu çalışma zamanına bırakmaz; **katı eşzamanlılık denetimi (strict concurrency checking)** yarışa yol açabilecek kodu derlemede reddeder.

Bu ders üç araç anlatır ve iki şey ölçer. Araçlar aktör yalıtımı, `Sendable` ve `@MainActor`'dır. Ölçümler bir aktörün tembel özelliğinin ne zaman kurulduğu ve `async let` ile başlatılan işin ne zaman koştuğudur. Aktörün içindeki sayaçlar düz `Int`'tir: `Probe` bir sınıftır, `Sendable` değildir ve aktör sınırını geçemez. Görevlerin koşma sırası belirsiz olduğundan sonuçlar toplanıp sıralanarak basılır; yalnız sayılar basılır.

```swift
// Probe.swift — bir ifadenin ne zaman degerlendirildigini kaydeder
final class Probe {
    let label: String
    private(set) var evaluations = 0
    private(set) var uses = 0
    private var evaluatedBeforeUse = false

    init(_ label: String) {
        self.label = label
    }

    func eval<T>(_ value: T) -> T {
        evaluations += 1
        if uses == 0 { evaluatedBeforeUse = true }
        return value
    }

    func use<T>(_ access: () -> T) -> T {
        uses += 1
        return access()
    }

    var moment: String {
        if evaluations == 0 { return "hic" }
        if evaluations > 1 { return "her-kullanimda" }
        return evaluatedBeforeUse ? "hemen" : "ilk-kullanimda"
    }

    func report() {
        print("\(label): \(evaluations) degerlendirme, \(uses) kullanim -> \(moment)")
    }
}

func tally(_ probes: [Probe]) {
    for moment in ["hemen", "ilk-kullanimda", "her-kullanimda", "hic"] {
        let count = probes.filter { $0.moment == moment }.count
        print("\(moment): \(count)")
    }
}
```

## Aktör Yalıtımı

**Aktör (actor)** durumunu kendi içinde saklayan ve bu duruma aynı anda tek bir görevin dokunmasına izin veren bir tiptir. Sınıfa benzer, ama değişebilir durumuna ve yalıtımlı yöntemlerine dışarıdan `await` yazmadan erişilemez; değişmez `let` özellikler ve `nonisolated` üyeler bunun dışındadır. Dışarıdaki bir görev aktörün yöntemini çağırırken `await` yazar: çağrı aktörün sırasına girer, aktör o an meşgulse görev bekler. Bu bekleme noktasına **askıya alma noktası (suspension point)** denir ve aktörün değişebilir durumuna `await` yazılmadan ulaşılamaz.

Aşağıdaki aktör iki sayaç tutar ve `total` adlı tembel bir özellik taşır. Özelliğin ifadesi bir yöntemi çağırır; yöntem kaç kez koştuğunu `builds` içinde sayar.

```swift
// Ledger.swift — tembel ozelligi olan aktor
actor Ledger {
    private(set) var builds = 0
    private(set) var reads = 0
    lazy var total: Int = makeTotal()

    private func makeTotal() -> Int {
        builds += 1
        return 42
    }

    func read() -> Int {
        reads += 1
        return total
    }
}
```

Ölçüm önce özelliğe hiç dokunmadan sayaçlara bakar, sonra dört görevi aynı anda `read` çağırmak üzere başlatır.

```swift
// main.swift — aktordeki lazy ozellik eszamanli okumalarda bir kez kurulur
let ledger = Ledger()
print("onceki kurulum \(await ledger.builds)")

let values = await withTaskGroup(of: Int.self) { group in
    for _ in 1...4 {
        group.addTask { await ledger.read() }
    }
    var all: [Int] = []
    for await value in group {
        all.append(value)
    }
    return all
}

print(values.sorted())
print("okuma \(await ledger.reads), kurulum \(await ledger.builds)")
```

```
onceki kurulum 0
[42, 42, 42, 42]
okuma 4, kurulum 1
```

Dört görev aynı özelliği okudu ama kurulum bir kez yapıldı. Özelliğe dokunulmadan önce kurulum sıfırdı: tembel ifade tanım satırında değil, ilk okumada çalıştı. Aktör yalıtımı burada iki işi birden yapar. Okumaları sıraya dizer ve ilk okumanın kurulumu bitmeden ikincisinin başlamasına izin vermez. Bu yüzden iki görevin aynı anda tembel ifadeyi çalıştırması ve sayacın bozulması olanaksızdır. Bu dersin anı **ilk kullanımda**dır.

Bu ölçümün ne söylemediğini de belirtmek gerekir. Aktör, işleri aynı anda yürütmeyi engellemez; yalnız aktörün durumuna dokunan kısmı sıraya dizer. Dört görev aynı anda başlar, ama `read` yöntemine tek tek girer. Tembel özelliğin tek kez kurulması bu sıralamanın doğrudan sonucudur: ikinci görev girdiğinde özellik zaten kuruludur.

Askıya alma noktasının bir bedeli vardır. Bir aktör yöntemi `await` ile başka bir işi beklerken aktör boşalır ve sıradaki görev içeri girebilir. Bu yüzden `await` öncesinde okunan bir değerin sonrasında aynı kaldığı varsayılmaz. Yalıtım iki görevin aynı anda içeride olmasını engeller; beklerken durumun değişmeyeceğini garanti etmez. Bu dersin ölçümlerinde yöntemlerin içinde `await` bulunmaz, bu yüzden bu durum ölçülmedi.

## Aktarılabilir Tip ve Ana Aktör

Bir değer görevler arasında gidip geliyorsa bir yalıtım sınırını geçiyordur. Sınırı geçebilen tiplere **`Sendable`** denir; iş adıyla aktarılabilir tip. Sayılar, dizgiler ve yalnız aktarılabilir tipleri saklayan, genel (public) olmayan değer tipleri bu özelliği kendiliğinden taşır; genel tiplerde uyum açıkça yazılır. Aktörler de taşır, çünkü kendi içlerini zaten korurlar. Değiştirilebilir durum tutan sıradan bir sınıf ise taşımaz: iki görev aynı örneğe aynı anda yazabilir.

`Probe` bu yüzden aktör sınırını geçemez. Aşağıdaki parça yalnız kaynaktır ve çalıştırılmaz: bir `Probe`, başka bir görevin kapanışına yakalanır. Swift 6 bunu derlemede reddeder; hata metni burada basılmaz.

```swift
// Kaynak parca: calistirilmaz
let moved = Probe("tasinan")
let work = Task.detached { moved.eval(1) }
print(await work.value)
```

Neden reddedildiği bellidir: `Task.detached` ile başlayan iş ana aktörden bağımsız koşar, `moved` ise ana aktörün durumudur. Aynı örneğe iki yerden aynı anda dokunulabilirdi ve bu tam olarak veri yarışının tanımıdır. Derleyici örneğin hangi satırda iki yalıtım bölgesine sızdığını gösterir. Çözümlerden biri değeri taşımamak ya da onu yalıtım altına almaktır: sayacı bir aktörün içine koymak, bu dersteki ölçümlerin yaptığı gibi. Derleyici sorunu çalıştırmadan önce yakaladığı için hata, programın bozuk çalıştığı bir gün yerine yazıldığı gün görülür.

**Ana aktör (`@MainActor`)** önceden tanımlı, tek bir yalıtım bölgesidir. `@MainActor` ile işaretlenen bir tipin durumuna yalnız ana aktörde koşan kod dokunabilir; başka bir yerden gelen çağrı `await` ister. Bir betiğin üst düzey kodu da ana aktörde koşar. Aşağıdaki sınıf bu işareti taşır ve bir sayaç tutar.

```swift
// Screen.swift — ana aktore baglanan sinif
@MainActor
final class Screen {
    private(set) var taps = 0

    func tap() {
        taps += 1
    }
}
```

Ölçüm beş görevi arka planda başlatır; her biri `tap` yöntemini `await` ile çağırır.

```swift
// main.swift — ana aktore bagli sinifa baska gorevlerden await ile erisilir
let screen = Screen()

await withTaskGroup(of: Void.self) { group in
    for _ in 1...5 {
        group.addTask { await screen.tap() }
    }
}

print("dokunus \(screen.taps)")
```

```
dokunus 5
```

Beş çağrı beş artış verdi, kayıp yok. Çağrılar ana aktörün sırasına girdi ve tek tek işlendi. `screen` bir sınıf olduğu halde görevlere yakalanabildi: ana aktöre bağlı sınıflar aktarılabilir sayılır, çünkü durumlarına yalnız tek bir yalıtım bölgesinden dokunulur.

## Görev Grupları

Yukarıdaki ölçümler **görev grubu (task group)** kullandı. Grup, sayısı çalışma zamanında belirlenebilen alt görevleri başlatır ve hepsinin bitmesini bekler; `for await` ile sonuçlar bittikçe toplanır. Sonuçların geliş sırası belirsizdir, bu yüzden dersin ölçümleri onları sıralayarak basar. Sayısı derleme anında bilinen birkaç iş için ise `async let` vardır; her bağlama tek bir alt görevdir; sonucu bağlamanın adıyla `await` edilerek okunur. Bir `async let` bağlaması sağ tarafındaki ifadeyi bir alt görev olarak başlatır; sonuç `await` ile okunur.

## Sınırlayıcı Ölçüm

Soru şudur: `async let` ne zaman başlar. Akla gelen yanıt, sonucun istendiği satırdır, yani `await` yazılan yer. Bunu sınamak için bir bağlama sonucu okunur, ikincisi hiç okunmaz. Aktör iki bağlamanın da `bump` yöntemini çağırıp çağırmadığını sayar.

```swift
// Counter.swift — cagri sayisini tutan aktor
actor Counter {
    private(set) var calls = 0

    func bump() -> Int {
        calls += 1
        return calls
    }
}
```

```swift
// main.swift — async let kuruldugu satirda baslar, beklenmese de
let counter = Counter()

do {
    async let waited = counter.bump()
    async let ignored = counter.bump()
    _ = await waited
}

print("cagri \(await counter.calls)")
```

```
cagri 2
```

İki çağrı sayıldı. `ignored` hiç `await` edilmedi, ama kapsam bitmeden iş yine koştu: sonucun beklenmemesi işin koşmasına engel olmadı. Kapsam çıkışında Swift beklenmeyen alt görevi iptal eder ve bitmesini bekler; `bump` iptale bakmadığı için çalışmasını tamamlar. Bu ölçüm bir sınırı gösterir. Sıradan bir çağrıda ifade, çağrının yazıldığı yerde ve çağıran beklenerek değerlendirilir; `async let` ise beklemeden bir alt görev başlatır. İki kural farklı alanlara aittir: `async let` bir ifade değil bir görevdir, ve yan etkisi sonucun okunup okunmamasından bağımsızdır. Bu yüzden `async let` için değerlendirme kullanımı beklemez.

**BL6.** Askıya alma noktası içeren aktör yöntemleri ölçülmedi. Ölçümler tek bir aktör ve dört ya da beş eşzamanlı görevle yapıldı; çok daha kalabalık gruplarda aynı kuralın geçtiği gösterilmedi. Görevlerin hangi sırayla koştuğu ölçülmedi, yalnız sayılar okundu. İptal edilen bir görevin iptale bakan bir yöntemde yarıda kaldığı durum ölçülmedi. Ölçüm yalnız işin beklenmese de koştuğunu gösterir; işin bağlama satırında mı yoksa daha sonra mı başladığı bu ölçümle ayrılamaz.

## Özet

- Aktör, durumunu kendi içinde korur ve dışarıdan erişimi `await` ile sıraya dizer; aktörün değişebilir durumuna `await` yazmadan erişmek mümkün değildir.
- Aktördeki `lazy` özellik birden çok eşzamanlı görevden okunsa da bir kez değerlendirilir ve ilk okumada kurulur.
- `Sendable` olmayan bir değeri görevler arasında taşımak derlemede reddedilir; çözümlerden biri, bu derste ölçülen, değeri yalıtım altına almaktır.
- `async let` bağlaması bir alt görev başlatır; sonucu hiç beklenmese de iş kapsam bitmeden koşar.
- Bu dersin anı: **ilk kullanımda**. Aktörün tembel özelliği eşzamanlı okumalarda yalnız ilk okumada, bir kez kurulur.

## Kurs Kapanışı

Bu kursun yirmi dört dersi tek bir soruyu yanıtladı: bir ifade ne zaman değerlendirilir. Her ders bir ifadeyi ölçtü ve anını dört addan biriyle söyledi. Tabloda ders, ölçülen ifade, an ve o dersin sınırı yan yana durur.

| Ders | Ölçülen ifade | An | Sınır |
|---|---|---|---|
| Swift'in Tasarım Hedefleri | `let` sabitinin sağ tarafı | `hemen` | Aynı ifade tek `Probe`'a iki kez bağlanırsa kural `her-kullanimda` der |
| Sabitler ve Değişkenler | Yerel sabitin ilk değeri | `hemen` | Başka dosyadaki küresel sabit ilk erişimde kurulur |
| Temel Tipler | `let` satırındaki tip dönüşümü | `hemen` | Okunmayan demet elemanı da değerlendirilir |
| Dizgi İşlemleri | İfade yerleştirmedeki `description` çağrısı | `her-kullanimda` | Bir `let` içinde saklanan dizgi bir kez kurulur |
| İşleçler | Sol taraf doğruyken `\|\|` işlecinin sağı | `hic` | Sağ tarafı `@autoclosure` ile almayan fonksiyon onu çağrıdan önce değerlendirir |
| Denetim Akışı | Eşleşmeden sonraki `where` koşulu | `hic` | Döngüde koşul her turda, `while` çıkışta bir kez daha sorulur |
| Fonksiyonlar | Varsayılan argüman ifadesi | `her-kullanimda` | Verilen argüman gövde onu okumasa da çağrıdan önce değerlendirilir |
| Kapanışlar | Kapanış gövdesi | `her-kullanimda` | Yakalama listesindeki ifade kurulurken bir kez değerlendirilir |
| İsteğe Bağlı Değerler | Boş kutuda `map` dönüştürücüsü | `hic` | Dış katmanı dolu `.some(nil)` kutusunda dönüştürücü koşar |
| İsteğe Bağlı Açma | `if let` sağ tarafı | `hemen` | Zincirin boş halkasından sonrası atlanır, aynı satırdaki bağımsız ifade atlanmaz |
| Yapılar ve Sınıflar | Yazarken kopyalanan belgenin kopyası | `ilk-kullanimda` | Tek sahipli ya da sınıf alanındaki belgede kopya hiç oluşmaz |
| Numaralandırmalar | İlişkili değerin ifadesi | `hemen` | Numaralandırmanın hesaplanan özelliği her okunuşta koşar |
| Özellikler | `lazy` özelliğin ilk değeri | `ilk-kullanimda` | Yapıda her kopya kendi `lazy` değerini ayrıca kurar |
| Protokoller | Protokol uzantısındaki varsayılan gerçekleştirim | `her-kullanimda` | Anı protokol değil, gereksinimi karşılayan tip belirler |
| Uzantılar | Uzantıyla eklenen hesaplanan özellik ve yöntem | `her-kullanimda` | Uzantıdaki `static let` ilk erişimde bir kez kurulur |
| Genellikler | `lazy.map` dönüştürücüsü | `her-kullanimda` | Tembel dizinin okunmayan elemanı hiç dönüştürülmez |
| Hata Yönetimi | `throw` sonrasındaki ifade | `hic` | `defer` bloğu her çıkışta koşar |
| Erişim Denetimi | `private lazy` önbellek | `ilk-kullanimda` | Anı erişim düzeyi değil, özelliğin çeşidi belirler |
| Otomatik Referans Sayımı | `deinit` gövdesi | `her-kullanimda` | İkinci referans varken bırakma `deinit`'i koşturmaz |
| Güçlü, Zayıf ve Sahipsiz Referanslar | Tutma döngüsündeki `deinit` gövdesi | `hic` | Gitmiş örneğe `unowned` okuma ölçülemez, programı durdurur |
| Kapanışlarda Yakalama | Yakalama listesindeki ifade | `hemen` | Saklanmayan kapanış döngü kurmaz |
| Özellik Sarmalayıcıları | `wrappedValue` okuyucusu | `her-kullanimda` | `@autoclosure` alan sarmalayıcı anı `ilk-kullanimda`'ya çevirir |
| Sonuç Kurucular | Seçilmeyen dalın ifadesi | `hic` | Hangi dalın seçileceği koşumda belirlenir |
| Eşzamanlılık Güvenliği | Aktördeki `lazy` özellik | `ilk-kullanimda` | Sonucu beklenmeyen `async let` yine koşar |

Yirmi dört dersin dağılımı şöyledir: altısı `hemen`, dördü `ilk-kullanimda`, sekizi `her-kullanimda`, altısı `hic`. Bu sayım tablonun an sütunundan çıkar. Kursun çekirdeğindeki sekiz ifadenin dağılımı ise 2, 1, 3 ve 2'dir. İkisi aynı şey değildir: çekirdek sekiz ifadeyi, tablo yirmi dört dersi sayar. Bir dersin anı o dersin ölçtüğü ifadeye aittir.

Kursun bitirdiği iş, ifadeyi kuralıyla birlikte okumayı öğretmektir. Aynı ifade yazılışına göre dört andan her birini verebilir: yerel sabit `hemen`, hesaplanan özellik `her-kullanimda`, `lazy` özellik `ilk-kullanimda`, seçilmeyen dal `hic`. Kursun bıraktığı alışkanlık, bir ifadeye bakınca onun yazılışından anını tahmin etmeye çalışmak yerine hangi kuralın altında durduğunu sormaktır. Bu kurs dilin kendisini ölçtü.
