Actor — nó là gì, cô lập dữ liệu và đồng thời

Tác giả: IT Sectr Đã đăng: 2026-06-18 Thời gian đọc: 11 phút

Actor là một kiểu được giới thiệu trong Swift 5.5+ giúp giải quyết vấn đề điều kiện tranh chấp dữ liệu ở cấp độ ngôn ngữ. Khác với các khóa thủ công và hàng đợi DispatchQueue, actor tự động cô lập trạng thái của nó và đồng bộ hóa quyền truy cập vào nó. Điều này có nghĩa là hai luồng không thể đồng thời sửa đổi cùng một thuộc tính của kiểu actor, loại bỏ điều kiện tranh chấp mà không cần nỗ lực thêm từ nhà phát triển. Cơ chế này dựa trên khái niệm cô lập actor, nơi trình biên dịch theo dõi quyền truy cập vào các thuộc tính và phương thức của actor. Theo Apple, 2025, mô hình actor là một phần cơ bản của hệ thống đồng thời của Swift.

Những điểm chính

  • Actor là một kiểu tự động cô lập trạng thái của nó khỏi các điều kiện tranh chấp ở cấp trình biên dịch
  • Cô lập đảm bảo chỉ một luồng truy cập vào thể hiện actor tại một thời điểm
  • Các cuộc gọi đến phương thức actor được thực thi không đồng bộ qua cơ chế async/await
  • Nonisolated cho phép tắt cô lập có chọn lọc cho các thành viên actor riêng lẻ
  • Reentrancy cho phép nhập lại vào một actor, ngăn chặn bế tắc

Actor trong Swift là gì?

Actor là một kiểu tham chiếu, tương tự như lớp (class), nhưng có khả năng bảo vệ tự động chống lại các điều kiện tranh chấp dữ liệu. Nó được giới thiệu trong Swift 5.5 như một phần của hệ thống đồng thời cùng với async/await và Task. Actor đảm bảo rằng trạng thái có thể thay đổi của nó sẽ không bao giờ bị đọc hoặc ghi đồng thời từ các luồng khác nhau mà không có đồng bộ hóa rõ ràng.

Cú pháp khai báo Actor

Để khai báo một actor, sử dụng từ khóa actor theo sau là dấu ngoặc nhọn với các thành viên của nó. Về mặt cú pháp, actor giống một lớp, nhưng hành vi của nó khác biệt hoàn toàn.

swift
actor BankAccount {
    private var balance: Double
    
    init(initialBalance: Double) {
        self.balance = initialBalance
    }
    
    func deposit(amount: Double) {
        balance += amount
    }
    
    func getBalance() -> Double {
        return balance
    }
}

Trình biên dịch tự động cô lập tất cả các thuộc tính và phương thức của actor để chúng chỉ có thể truy cập được trong ngữ cảnh actor. Khi cố gắng truy cập balance từ bên ngoài actor, trình biên dịch sẽ tạo ra lỗi nếu cuộc gọi không được đánh dấu là async.

Cô lập dữ liệu trong Actor hoạt động như thế nào

Cô lập dữ liệu là khái niệm chính của actor. Actor đảm bảo quyền truy cập loại trừ lẫn nhau vào trạng thái của nó thông qua cơ chế thực thi actor. Mỗi actor có bộ thực thi riêng xử lý tuần tự tất cả các truy cập vào các thành viên được cô lập của nó.

Cách thức hoạt động của cô lập

Khi mã bên ngoài actor gọi phương thức của nó, cuộc gọi được đặt vào hàng đợi của bộ thực thi actor. Actor chỉ thực thi một tác vụ tại một thời điểm, đảm bảo không có điều kiện tranh chấp. Nếu hai luồng cùng gọi deposit, cuộc gọi thứ hai sẽ chờ cuộc gọi đầu tiên hoàn thành.

swift
let account = BankAccount(initialBalance: 1000.0)
// Cuộc gọi không đồng bộ — yêu cầu từ bên ngoài actor
await account.deposit(amount: 500.0)
let currentBalance = await account.getBalance()

Mỗi cuộc gọi đến phương thức actor đều yêu cầu await vì actor có thể đang bận với một tác vụ khác. Đây không phải là lỗi mà là một thiết kế có chủ đích nhằm ngăn chặn các điều kiện tranh chấp. Swift ngầm làm cho các getter và setter của thuộc tính actor trở thành async, vì vậy việc đọc một thuộc tính cũng yêu cầu await.

Sự khác biệt chính giữa Actor và Class

Actor và lớp (class) đều là kiểu tham chiếu, nhưng hành vi của chúng trong môi trường đa luồng khác nhau hoàn toàn. Một class không cung cấp bất kỳ khả năng bảo vệ tự động nào chống lại các điều kiện tranh chấp, trong khi actor tích hợp nó ở cấp trình biên dịch thông qua hệ thống kiểu.

Đặc điểmActorClass
Bảo vệ tranh chấpTự động, ở cấp trình biên dịchYêu cầu đồng bộ hóa thủ công
Truy cập thuộc tínhChỉ qua await từ bên ngoàiTrực tiếp, không đồng bộ hóa
Kế thừaChỉ từ các actor khácKế thừa lớp tiêu chuẩn
Tuân thủ giao thứcCó thể tuân thủ giao thứcTiêu chuẩn
Hiệu suấtChi phí thấp với cô lậpNhanh hơn không đồng bộ hóa

Kế thừa Actor

Một actor chỉ có thể kế thừa từ một actor khác và không thể kế thừa từ một class. Điều này có chủ đích vì class thiếu cơ chế cô lập actor và việc trộn lẫn các kiểu sẽ phá vỡ các đảm bảo an toàn.

swift
actor SavingsAccount: BankAccount {
    func applyInterest(rate: Double) {
        let interest = balance * rate
        balance += interest
    }
}

Gọi không đồng bộ các phương thức Actor

Các cuộc gọi không đồng bộ là cơ chế tương tác với actor từ mã bên ngoài. Vì actor cô lập trạng thái của nó, bất kỳ truy cập nào vào các thành viên của nó từ bên ngoài đều yêu cầu await. Điều này cho phép Swift đảm bảo rằng mã gọi không chặn luồng và actor có thể xử lý các yêu cầu khác.

Actor toàn cục và cục bộ

Ngoài các kiểu actor đã khai báo, Swift hỗ trợ actor toàn cục — thuộc tính @MainActor, đánh dấu các lớp, thuộc tính hoặc phương thức có thể thực thi trên luồng chính. Điều này đặc biệt hữu ích cho mã UI trong các ứng dụng iOS.

swift
@MainActor
class ViewModel: ObservableObject {
    @Published var title: String = ""
    
    func updateTitle() {
        // Mã này được đảm bảo chạy trên luồng chính
        title = "New Title"
    }
}

Sử dụng @MainActor loại bỏ nhu cầu gọi thủ công DispatchQueue.main.async, làm cho mã sạch hơn và an toàn hơn. Trình biên dịch đảm bảo rằng việc chuyển sang luồng chính diễn ra chính xác.

Ngữ cảnh Nonisolated và Actor-Isolated

Nonisolated là một từ khóa cho phép đánh dấu một phương thức hoặc thuộc tính được tính toán của actor là không bị cô lập. Các thành viên này không có quyền truy cập vào trạng thái bị cô lập của actor, nhưng có thể được gọi mà không cần await từ bên ngoài actor.

Hàm Nonisolated

Các phương thức nonisolated hữu ích cho các tính toán không phụ thuộc vào trạng thái có thể thay đổi của actor. Ví dụ, phương thức formatBalance không truy cập trực tiếp vào balance, mà chỉ định dạng giá trị được truyền vào — phương thức như vậy an toàn để đặt là nonisolated.

swift
actor BankAccount {
    private var balance: Double = 0
    
    nonisolated func formatBalance(amount: Double) -> String {
        return "$\(amount)"
    }
}

Các thành viên nonisolated thực thi đồng bộ và không yêu cầu await. Tuy nhiên, chúng không thể đọc trực tiếp các thuộc tính bị cô lập của actor. Nếu một phương thức nonisolated cần một giá trị từ actor, nó phải được truyền dưới dạng tham số.

Reentrancy và nhập lại

Reentrancy là một cơ chế cho phép nhập lại vào một actor trong khi chờ một cuộc gọi không đồng bộ. Nếu không có reentrancy, một actor có thể bị bế tắc vĩnh viễn nếu một trong các phương thức của nó chờ một phương thức khác mà phương thức đó lại chờ phương thức đầu tiên.

Cách thức hoạt động của nhập lại

Khi mã bên trong actor thực thi await, actor tạm dừng tác vụ hiện tại và có thể xử lý một tác vụ khác trong hàng đợi. Sau khi await hoàn thành, tác vụ được tiếp tục. Điều này ngăn chặn bế tắc nhưng đòi hỏi sự thận trọng: trạng thái của actor giữa các điểm await có thể thay đổi.

swift
actor DataProcessor {
    var cache: [Int: String] = [:]
    
    func process(id: Int) async -> String {
        if let cached = cache[id] {
            return cached
        }
        // await — điểm nhập lại
        let result = await fetchData(id: id)
        // Bộ nhớ đệm có thể đã thay đổi sau await — kiểm tra lại
        cache[id] = result
        return result
    }
}

Các nhà phát triển phải tính đến reentrancy và kiểm tra trạng thái của actor sau các điểm await. Một lỗi điển hình là cho rằng cô lập actor tồn tại qua các điểm tạm dừng không đồng bộ. Trong thực tế, giữa await và câu lệnh tiếp theo, trạng thái có thể khác so với dự kiến.

Câu hỏi thường gặp

Actor khác với class trong Swift như thế nào?

Actor tự động cô lập trạng thái của nó khỏi các điều kiện tranh chấp, yêu cầu await để truy cập từ bên ngoài. Class không cung cấp sự bảo vệ như vậy — nhà phát triển tự chịu trách nhiệm đồng bộ hóa qua khóa hoặc hàng đợi. Actor chỉ kế thừa từ actor, class kế thừa từ class.

Có thể kế thừa từ Actor không?

Có, một actor có thể kế thừa từ một actor khác. Lớp con nhận được tất cả các thuộc tính và phương thức được cô lập của cha. Actor không thể kế thừa từ class vì các class thiếu cơ chế cô lập actor ở cấp trình biên dịch.

Ngữ cảnh actor-isolated là gì?

Ngữ cảnh actor-isolated là một vùng mã nơi cho phép truy cập trực tiếp vào trạng thái có thể thay đổi của actor. Bên trong các phương thức actor được đánh dấu là bị cô lập (mặc định), có thể đọc và ghi các thuộc tính mà không cần await. Trình biên dịch kiểm tra các ranh giới cô lập.

Làm thế nào để truyền dữ liệu từ Actor ra mã bên ngoài?

Dữ liệu từ actor được truyền qua phương thức async trả về kiểu Sendable, hoặc qua phương thức nonisolated chấp nhận giá trị dưới dạng tham số. Cũng có thể tạo một thuộc tính async trả về ảnh chụp nhanh trạng thái của actor dưới dạng cấu trúc Sendable.

Actor có hỗ trợ tuân thủ giao thức không?

Có, actor có thể tuân thủ các giao thức. Nếu một giao thức chứa yêu cầu bị cô lập (actor-isolated), chúng tự động trở thành bị cô lập với actor. Đối với các phương thức không đồng bộ trong giao thức, có thể chỉ định rằng chúng phải được gọi trên một actor cụ thể bằng cách sử dụng đánh dấu isolated.

Tổng kết

  • Actor là kiểu tham chiếu với cô lập dữ liệu tự động, được giới thiệu trong Swift 5.5+
  • Cô lập hoạt động thông qua bộ thực thi actor xử lý các yêu cầu tuần tự
  • Truy cập bên ngoài vào actor yêu cầu await — điều này đảm bảo không có điều kiện tranh chấp
  • Nonisolated cho phép khai báo các phương thức đồng bộ không truy cập trạng thái bị cô lập
  • Actor toàn cục (@MainActor) mở rộng cô lập ra toàn bộ luồng thực thi
  • Reentrancy ngăn chặn bế tắc bằng cách cho phép nhập lại vào actor trong await

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm