---
title: Aktörler
source: 'https://academia.sh/tr/kurslar/ios-veri-ve-ag/aktorler'
course: 'Veri, Ağ ve Eşzamanlılık'
language: tr
updated: '2026-10-21T05:03:08+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Aktörün durumu yalıtarak aynı anda yalnız bir işe erişim verdiğini açıklar'
    - 'İlk talebi kabul eden bir koltuk haritasının talep sırasına duyarlı olduğunu ölçer'
    - 'Her çağrıda artan bir sayacın yinelemeye duyarlı olduğunu ölçer'
    - 'Yalıtımın veri yarışını önlediğini, ama sırayı sabitlemediğini ayırır'
---

# Aktörler

Aktör yalıtımının tek seferde tek işe izin verdiği, ama iki isteğin sırasının ve bir sayacın yinelenmesinin sonucu yine değiştirdiğinin ölçümü.

Önceki derste görev grubunun sonuçlarını kümede toplamak geliş sırasını sildi, diziye dökmek ise sırayı sonuca yazdı. Orada sorun toplamanın biçimiydi. Bu derste sorun durumun kendisidir: birden çok iş aynı veriye ulaşırsa yalıtımın verdiği güvence neyi kapsar, neyi kapsamaz?

Çekirdek dosya konunun her dersinde aynıdır.

```swift
// Replay.swift — bir islemin yinelemeye ve siraya duyarliligini siniflandirir
struct Trial<State: Equatable & CustomStringConvertible> {
    let label: String
    let once: State
    let twice: State
    let forward: State
    let backward: State

    var sensitivity: String {
        switch (once != twice, forward != backward) {
        case (false, false): return "saglam"
        case (true, false): return "yinelemeye-duyarli"
        case (false, true): return "siraya-duyarli"
        case (true, true): return "cift-duyarli"
        }
    }

    func report() -> String {
        print("\(label) | bir=\(once) iki=\(twice) ab=\(forward) ba=\(backward) -> \(sensitivity)")
        return sensitivity
    }
}

func replay<State>(_ label: String, from start: State, _ a: (State) -> State, then b: (State) -> State) -> Trial<State> {
    Trial(label: label, once: a(start), twice: a(a(start)), forward: b(a(start)), backward: a(b(start)))
}

func countSensitivities(_ results: [String]) {
    for name in ["saglam", "yinelemeye-duyarli", "siraya-duyarli", "cift-duyarli"] {
        print("\(name): \(results.filter { $0 == name }.count)")
    }
}
```

Aktör işlemleri eşzamansızdır; çağrılar `await` gerektirir. Bu yüzden `replay` yerine her koşum elle kurulur ve `Trial`'a doğrudan verilir. Model, işlemin duruma ne yaptığını kurar; ağın ya da çalışma zamanının kendisi değildir.

## Aktör ve Yalıtım

Bir **aktör** (`actor`), kendi değişebilir durumunu koruyan bir referans tipidir. Aktörün durumuna dışarıdan doğrudan dokunulmaz; erişim `await` ile aktörün bir işlevine girerek yapılır ve aktör aynı anda yalnız bir işi yürütür. Buna **aktör yalıtımı** denir. Yalıtımın kazandırdığı şey **veri yarışının** (data race) önlenmesidir: iki iş aynı bellek alanını aynı anda okuyup yazamaz.

Bununla bir **yarış koşulu** (race condition) arasındaki fark önemlidir. Veri yarışı bellek düzeyinde bir hatadır ve yalıtım onu ortadan kaldırır. Yarış koşulu ise sonucun işlerin sırasına bağlı olmasıdır ve yalıtım bunu çözmez: iki iş sırayla, tek tek, güvenle çalışır; ama hangisinin önce çalıştığı yine sonucu değiştirir.

```swift
// Seats.swift — koltuk haritasi ve bilet sayaci
actor SeatMap {
    private(set) var owner = "-"

    func claim(_ name: String) -> Bool {
        if owner == "-" {
            owner = name
            return true
        }
        return false
    }

    func currentOwner() -> String { owner }
}

actor TicketCounter {
    private(set) var sold = 0

    func sell() { sold += 1 }
    func total() -> Int { sold }
}

func runClaims(_ names: [String]) async -> String {
    let map = SeatMap()
    for name in names { _ = await map.claim(name) }
    return await map.currentOwner()
}

func runSales(_ times: Int) async -> Int {
    let counter = TicketCounter()
    for _ in 0..<times { await counter.sell() }
    return await counter.total()
}
```

`SeatMap` tek bir koltuğun sahibini tutar. İlk talep eden kazanır; sonraki talepler reddedilir. `TicketCounter` her çağrıda satış sayısını bir artırır. İkisi de aktördür; ikisinde de yalıtım tamdır.

## İlk Talep Kazanır ve Sayaç Artar

Koltuk haritası iki ad için dört koşumda ölçülür: tek talep, aynı adın iki talebi, `ayse` sonra `burak`, `burak` sonra `ayse`. Sayaç ise bir ve iki satışla ölçülür; iki farklı işlem olmadığı için ileri ve geri sıra iki satıştır.

```swift
// main.swift — koltuk talepleri ve bilet satislari
let claimOnce = await runClaims(["ayse"])
let claimTwice = await runClaims(["ayse", "ayse"])
let claimForward = await runClaims(["ayse", "burak"])
let claimBackward = await runClaims(["burak", "ayse"])
let seat = Trial(label: "koltuk-talebi", once: claimOnce, twice: claimTwice, forward: claimForward, backward: claimBackward).report()

let saleOnce = await runSales(1)
let saleTwice = await runSales(2)
let sales = Trial(label: "bilet-satisi", once: saleOnce, twice: saleTwice, forward: saleTwice, backward: saleTwice).report()

countSensitivities([seat, sales])
```

```
koltuk-talebi | bir=ayse iki=ayse ab=ayse ba=burak -> siraya-duyarli
bilet-satisi | bir=1 iki=2 ab=2 ba=2 -> yinelemeye-duyarli
saglam: 0
yinelemeye-duyarli: 1
siraya-duyarli: 1
cift-duyarli: 0
```

Koltuk satırında aynı adın ikinci talebi `ayse`'yi değiştirmez, çünkü ikinci talep reddedilir; `bir` ile `iki` aynıdır. Ama `ab` `ayse`, `ba` `burak`: ilk gelen kazanır ve kimin ilk geleceği sonuca yazılır. İşlem **sıraya duyarlıdır**. Sayaç satırında `bir=1` ve `iki=2`: her çağrı sayacı artırır, yineleme sonucu değiştirir. Sıra ise sayacı etkilemez; bilet satışları değişmeli olduğu için `ab` ile `ba` aynıdır. İşlem **yinelemeye duyarlıdır**. Sayım bir yinelemeye, bir sıraya duyarlı.

**ES7.** Bu ölçüm koltuk için tek bir sahip alanını modeller. Birden çok koltuğun ya da bir bekleme listesinin aynı kuralı nasıl etkilediği ölçülmedi.

**ES8.** Sayaç ölçümünde sıra, iki özdeş `sell` çağrısının sırası sayılır. Farklı türde iki işlemin birlikte kullanıldığı durum ölçülmedi.

## Aktör Neden Var

Aktörden önce paylaşılan durumu korumanın yolu kilitti: durumu okuyan ya da yazan her kod, önce bir kilit alır, işi bitirince bırakırdı. Yöntem çalışır, ama koruma programcının disiplinine bağlıdır. Kilidi almayı unutan tek bir satır, bütün korumayı geçersiz kılar ve derleyici bunu fark etmez. Hata da nadiren görünür: iki iş aynı anda o satıra denk geldiğinde, çoğu zaman yalnız yük altında, bazen yalnız bir müşterinin aygıtında.

Aktör korumayı dilin kendisine taşır. Durum aktörün içindedir ve ona erişmenin tek yolu aktörün işlevleridir; derleyici dışarıdan doğrudan erişimi reddeder. Kilidi unutmak mümkün olmaz, çünkü kilit alma işini aktör kendisi yapar. Karşılığında bir bedel vardır: aktöre her giriş bir `await` noktasıdır ve çağıran, aktör meşgulse sırada bekler. Bu bekleyiş çağıranı bloke etmez; çağıran durdurulur, iş parçacığı başka işe verilir. Aktör bu yüzden uzun ve yavaş işler için değil, kısa ve sık yapılan durum değişiklikleri için uygundur.

Tipik yanlış kurulum, aynı korumayı sınıf ile yapmaya çalışıp korumayı atlamaktır.

```swift
// Yanlis: degisebilir bir sinif, koruma yok
final class UnsafeTicketCounter {
    var sold = 0
    func sell() { sold += 1 }
}
```

Bu sınıf tek bir akıştan kullanıldığında doğru çalışır. İki görev aynı örneğe aynı anda `sell` çağırdığında ise `sold += 1` okuma, artırma ve yazma adımlarından oluştuğu için iki adım iç içe geçebilir ve bir satış kaybolur. Sınıf aktarılabilir olmadığı için Swift 6 dil kipi, bu örneği iki göreve paylaştırmanıza izin vermemelidir; bu derleyici davranışı burada ölçülmedi. Düzeltme, sınıfı `actor` yapmaktır; yukarıdaki `TicketCounter` tam olarak budur ve gövdesi değişmez.

## Aktarılabilir Tipler

Aktörün içindeki durum yalıtılmıştır, ama aktöre giren ve çıkan değerler sınırı geçer. Bir değerin eşzamanlı sınırlar arasında güvenle gönderilebilmesi için tipi **aktarılabilir** (`Sendable`) olmalıdır. Değer tipleri ve değişmez değerler genellikle bu şartı kendiliğinden karşılar; değişebilir referans tipleri karşılamaz ve derleyici bunu hata olarak bildirir.

```swift
// Aktarilabilir tip: aktorler arasinda gonderilebilen degisemez deger
struct Reservation: Sendable {
    let seat: Int
    let owner: String
}

func describe(_ reservation: Reservation) -> String {
    "koltuk \(reservation.seat) -> \(reservation.owner)"
}
```

`Reservation` yalnız sabit alanlar taşır, bu yüzden güvenle gönderilir. Değişmez değerler sınırı geçerken kopyalanır ya da paylaşılır; kimse onları aynı anda değiştiremez. Bu parça ölçülmez, çünkü hiçbir işlemin duruma ne yaptığını göstermez; yalnız tip kuralını anlatır.

## Koltuğu Bırakmak

Şimdiye dek koltuk yalnız kazanılıyordu. Gerçek bir rezervasyonda koltuk geri de bırakılır ve bırakma, kazanma ile birlikte sıraya duyarlılığın ikinci bir biçimini getirir. Burada bir kural var: yalnız koltuğun mevcut sahibi onu bırakabilir; başkasının bırakma isteği sessizce reddedilir.

```swift
// Release.swift — birakilabilir koltuk
actor ReleasableSeat {
    private(set) var owner = "-"

    func claim(_ name: String) {
        if owner == "-" { owner = name }
    }

    func release(_ name: String) {
        if owner == name { owner = "-" }
    }
}

func runSeatOperations(_ operations: [(isRelease: Bool, name: String)]) async -> String {
    let seat = ReleasableSeat()
    for operation in operations {
        if operation.isRelease {
            await seat.release(operation.name)
        } else {
            await seat.claim(operation.name)
        }
    }
    return await seat.owner
}
```

Ölçülen çift, `ayse` için kazanma ile bırakmadır. Dört koşum: tek kazanma, iki kazanma, kazanıp bırakma, bırakıp kazanma.

```swift
// main.swift — kazanma ve birakma
let releaseOnce = await runSeatOperations([(false, "ayse")])
let releaseTwice = await runSeatOperations([(false, "ayse"), (false, "ayse")])
let releaseForward = await runSeatOperations([(false, "ayse"), (true, "ayse")])
let releaseBackward = await runSeatOperations([(true, "ayse"), (false, "ayse")])
let released = Trial(label: "birakma", once: releaseOnce, twice: releaseTwice, forward: releaseForward, backward: releaseBackward).report()

countSensitivities([released])
```

```
birakma | bir=ayse iki=ayse ab=- ba=ayse -> siraya-duyarli
saglam: 0
yinelemeye-duyarli: 0
siraya-duyarli: 1
cift-duyarli: 0
```

Aynı kazanma ikinci kez yapılınca sonuç değişmez: `bir` ve `iki` ikisi de `ayse`. Ama kazanıp bırakmak koltuğu boşaltır (`ab` için `-`), bırakıp kazanmak ise koltuğu `ayse`'ye verir (`ba`). Bırakma, kazanmadan önce geldiğinde etkisizdir, çünkü bırakılacak bir sahiplik yoktur. İşlem çifti **sıraya duyarlıdır**. Bu, rezervasyon ekranlarında tanıdık bir hatanın modelidir: kullanıcı koltuğu seçer ve hemen vazgeçer; iki istek ağda yer değiştirirse koltuk, kullanıcı vazgeçtiği hâlde ona ait görünür.

**ES9.** Bu ölçümde bırakma, yalnız mevcut sahibin adıyla yapılan istek olarak kuruldu. Başka bir kullanıcının bırakma isteği ve süre aşımıyla otomatik bırakma ölçülmedi.

## Gerçek Uygulamada

Bir etkinlik uygulamasında koltuk seçimi ekranı düşünün. Kullanıcı bir koltuğa dokunur, uygulama sunucuya istek atar ve sonucu ekranda gösterir. İstemci tarafında koltukların durumunu bir aktör tutar: hangi koltuk boş, hangisi seçili, hangisi sunucudan henüz yanıt beklemede. Kullanıcı hızlı davranır, bir koltuğu seçip bırakır ve ardından başka birini seçer; yani birkaç istek kısa sürede art arda gönderilir. Aktör, durumun bu isteklerin ortasında bozulmadan kalmasını sağlar. Hangi isteğin önce işleneceğini ise aktör değil, isteklerin aktöre ulaşma sırası belirler.

Bu ayrım ekranda iki ayrı hata olarak görünür. Veri yarışı olsaydı uygulama bellek bozulmasıyla çökerdi; aktör bunu önler. Sıra hatasında ise uygulama çökmez, yalnız yanlış koltuğu seçili gösterir; aktör bunu önlemez, çünkü iki istek de ayrı ayrı geçerlidir. Bu yüzden sıra gerektiren akışlarda iki yoldan biri seçilir: istekler tek bir görevden, art arda `await` ile gönderilir, ya da her istek bir sıra numarası taşır ve aktör eski numaralı isteği reddeder.

Yaygın bir kurulum hatası, kontrolü aktörün dışında yapıp eylemi içinde yapmaktır.

```swift
// Yanlis: kontrol ile eylem arasinda baska is girebilir
func claimIfFree(_ map: SeatMap, _ name: String) async {
    if await map.currentOwner() == "-" {
        _ = await map.claim(name)
    }
}
```

İki `await` arasında aktöre başka bir görev girebilir: iki çağrı da koltuğu boş görür, ikisi de `claim` çağırır. `SeatMap.claim` kendi içinde kontrol edip yazdığı için ikinci çağrı reddedilir ve sonuç bu örnekte doğru kalır; ama kural aktörün dışına taşındığında, örneğin "boşsa bir ödeme isteği gönder" gibi yan etkili bir eylem eklendiğinde, ödeme iki kez gönderilebilir. Düzeltme, kontrolü ve eylemi aktörün tek bir işlevinde birleştirmektir; `claim` bunu zaten yapar. Bu parça ölçülmedi, çünkü iki çağrının araya girdiği bir yürütmeyi bu dersin ardışık koşumu kuramaz.

## Sık Yapılan Hatalar

- Aktörü yarış koşuluna karşı bağışık sanmak. Aktör veri yarışını önler; iki işin sırasını sabitlemez.
- Aktörün içinde durumu okuyup `await`'ten sonra yazmak. Duraklama noktasında başka bir iş araya girmiş olabilir.
- Aktör durumuna dışarıdan doğrudan erişmeye çalışmak. Erişim `await` ile yapılır; derleyici bunu zorlar.
- Aktarılabilir olmayan bir referans tipini aktöre vermek. Sınır geçiren her değerin tipi aktarılabilir olmalıdır.

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

Yeniden girilebilirlik (reentrancy) aktörün bir `await` noktasında durduğunda başka bir işin aynı aktöre girebilmesidir. Aşağıdaki koltuk haritası talebi bir duraklama noktasından sonra yazar. Ardışık koşumda aynı çağrılar sırayla çalışır ve araya hiçbir iş girmez; ölçüm bunu görmez.

```swift
// main.swift — duraklayan koltuk haritasi, ayni dort kosum
actor SlowSeatMap {
    private(set) var owner = "-"

    func pause() async {}

    func claim(_ name: String) async -> Bool {
        if owner == "-" {
            await pause()
            owner = name
            return true
        }
        return false
    }
}

func runSlowClaims(_ names: [String]) async -> String {
    let map = SlowSeatMap()
    for name in names { _ = await map.claim(name) }
    return await map.owner
}

let slowOnce = await runSlowClaims(["ayse"])
let slowTwice = await runSlowClaims(["ayse", "ayse"])
let slowForward = await runSlowClaims(["ayse", "burak"])
let slowBackward = await runSlowClaims(["burak", "ayse"])
let slow = Trial(label: "yavas-koltuk", once: slowOnce, twice: slowTwice, forward: slowForward, backward: slowBackward).report()

countSensitivities([slow])
```

```
yavas-koltuk | bir=ayse iki=ayse ab=ayse ba=burak -> siraya-duyarli
saglam: 0
yinelemeye-duyarli: 0
siraya-duyarli: 1
cift-duyarli: 0
```

Duraklama noktası eklendi ve satır `koltuk-talebi` ile aynı çıktı. Kural, bir `await` arasına başka iş girmediğini görmez; yalnız dört koşumun sonunda durumun eşit olup olmadığına bakar. Üstelik aktör veri yarışından yoksundur ve bu, sonucu sağlam yapmaz: aynı satır **sıraya duyarlı** kalır. Kural nedeni görmez, eşitsizliği görür. İki isteğin aynı anda duraklayıp ikisinin de boş koltuğu gördüğü durum burada kurulmadı ve ölçülmedi; o durumda koltuk iki kez kazanılmış olurdu. Sayım bir sıraya duyarlı.

**ES10.** Duraklama noktasında araya başka bir iş girmesi bu derste hiçbir ölçümde kurulmadı. Yeniden girilebilirlik burada yalnız anlatıldı.

Bir kural olarak, aktörün işlevi tek adımda karar verip yazıyorsa durum tutarlı kalır; kararı dışarıda verip aktöre yalnız yazdırıyorsanız tutarlılık sizin sorumluluğunuza geri döner. Bu ayrım küçük görünür, ama rezervasyon, stok ve bakiye gibi alanlarda hatanın nerede çıkacağını belirler.

## Özet

- Aktör, durumunu yalıtır ve aynı anda tek işe izin verir; bu veri yarışını önler.
- Yalıtım, iki işin sırasını sabitlemez: ilk talebi kabul eden koltuk haritası sıraya duyarlıdır.
- Her çağrıda artan sayaç yinelemeye duyarlıdır ve sırayı görmez.
- Aktarılabilir tipler sınırı güvenle geçer; değişebilir referanslar geçemez.
- Duraklama noktası eklemek ardışık ölçümde sınıfı değiştirmez; araya girme ölçülmedi.
- Bu dersin duyarlılığı: **sıraya duyarlı** (koltuk) ve **yinelemeye duyarlı** (sayaç).

## Sonraki Adım

Aktörlerin içinde durumu korumak bir şey, arayüzün durumunu korumak başka bir şeydir. Sıradaki derste ana iş parçacığına bağlı çalışan kodu ve arama sonuçlarının geliş sırasına göre ekrana yazıldığı bir modeli ölçeceğiz.
