← Назад к вопросам

Что такое Actor?

2.0 Middle🔥 181 комментариев
#Многопоточность и асинхронность#Язык Swift

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Actor в Swift — потокобезопасность нового поколения

Что такое Actor

Actor — это новый тип в Swift 5.5+, который обеспечивает потокобезопасность автоматически. Это альтернатива старым подходам с locks и semaphores.

actor UserManager {
    private var users: [User] = []
    
    func addUser(_ user: User) {
        users.append(user)  // Потокобезопасно автоматически
    }
    
    func getUsers() -> [User] {
        users
    }
}

Главное отличие от класса:

  • Class может быть использован из нескольких потоков одновременно (race condition)
  • Actor гарантирует эксклюзивный доступ к своему состоянию

Как это работает

actor Counter {
    private var count = 0
    
    func increment() {
        count += 1  // Всегда безопасно, даже без locks
    }
    
    func getCount() -> Int {
        count
    }
}

// Использование
let counter = Counter()

// Нужно использовать await!
await counter.increment()  // Ждём доступа
let value = await counter.getCount()
print(value)

Компилятор требует await — это напоминает о потокобезопасности

Actor vs Class с NSLock

Старый подход (NSLock):

class OldCounter {
    private var count = 0
    private let lock = NSLock()
    
    func increment() {
        lock.lock()
        defer { lock.unlock() }
        count += 1
    }
}

Новый подход (Actor):

actor NewCounter {
    private var count = 0
    
    func increment() {
        count += 1  // Никаких locks!
    }
}

Аctor намного безопаснее и проще.

MainActor для UI

@MainActor
class UserViewModel: ObservableObject {
    @Published var users: [User] = []
    
    func loadUsers() {
        // Гарантирует выполнение на главном потоке
        DispatchQueue.global().async {
            let users = self.fetchUsers()  // фоне
            Task { @MainActor in
                self.users = users  // автоматически на главном потоке
            }
        }
    }
}

@MainActor — специальный Actor для главного потока

Практический пример: потокобезопасное хранилище

actor DataStore {
    private var data: [String: Any] = [:]
    private var accessCount = 0
    
    func set(key: String, value: Any) {
        data[key] = value
        accessCount += 1
    }
    
    func get(key: String) -> Any? {
        accessCount += 1
        return data[key]
    }
    
    func getStats() -> (count: Int, keys: Int) {
        (accessCount, data.count)
    }
}

// Использование
let store = DataStore()

for i in 0..<100 {
    Task {
        await store.set(key: "key\(i)", value: i)
    }
}

Task {
    let stats = await store.getStats()
    print("Access count: \(stats.count), Keys: \(stats.keys)")
}

Изоляция данных (Data Race Prevention)

// ❌ Ошибка компиляции!
class SharedData {
    var value: Int = 0
}

let shared = SharedData()

Task {
    shared.value = 1  // Data race!
}

Task {
    shared.value = 2  // Data race!
}

// ✅ Правильно
actor SharedData {
    var value: Int = 0
}

let shared = SharedData()

Task {
    await shared.set(1)  // Safe
}

Nonisolated членов Actor

actor User {
    let id: String  // Immutable, можно читать без await
    nonisolated let createdAt: Date  // Тоже безопасен
    var name: String  // Нужен await
    
    init(id: String, name: String) {
        self.id = id
        self.name = name
        self.createdAt = Date()
    }
    
    func updateName(_ newName: String) {
        self.name = newName
    }
}

let user = User(id: "123", name: "John")
print(user.id)  // Без await (immutable)
await user.updateName("Jane")  // С await (изменяемое)

SendableProtocol

// Можно отправлять между actors
struct Message: Sendable {
    let text: String
    let timestamp: Date
}

actor ChatRoom {
    private var messages: [Message] = []
    
    func send(_ message: Message) {
        messages.append(message)  // Message — Sendable
    }
}

Сравнение подходов

ПодходБезопасностьСложностьРекомендация
NSLockManualВысокая❌ Старый
Serial QueueGoodСредняя⚠️ Работает
ActorAutomaticНизкая✅ Лучший выбор

Когда использовать Actor

✅ Общее состояние, доступное из нескольких потоков ✅ Кэширование ✅ Database access wrappers ✅ Сетевые операции ✅ Любое потокобезопасное хранилище

❌ Immutable data — не нужен Actor ❌ Value types (struct) — уже потокобезопасны

Ключевые правила

✅ Используй Actor вместо NSLock и Serial Queue ✅ Всегда используй await при доступе к Actor ✅ Используй @MainActor для UI ✅ Помечай Sendable для типов, передаваемых между actors ❌ Не забывай про await — это не забывчивость, это безопасность

Actor — это будущее потокобезопасности в Swift. Это одна из лучших фич Swift 5.5+. Новый код должен использовать Actors вместо старых подходов.