OperationQueue là một hàng đợi tác vụ cấp cao trong iOS và macOS được xây dựng trên Grand Central Dispatch. Theo Apple Developer Documentation, 2026, OperationQueue quản lý các phiên bản của Operation — các đối tượng đóng gói một đơn vị công việc. Không giống như DispatchQueue, OperationQueue hỗ trợ phụ thuộc giữa các tác vụ, ưu tiên, quan sát KVO và hủy bỏ các tác vụ đang chạy. OperationQueue tự động quản lý một nhóm luồng, phân phối các tác vụ trên các tài nguyên hệ thống có sẵn.
Điểm Chính
OperationQueue là một lớp từ framework Foundation quản lý việc thực thi các đối tượng Operation. Không giống như DispatchQueue, OperationQueue không yêu cầu chỉ định rõ ràng chế độ nối tiếp hay đồng thời — số lượng tác vụ thực thi đồng thời được kiểm soát bởi thuộc tính maxConcurrentOperationCount. Giá trị 1 làm cho hàng đợi tuần tự, bất kỳ giá trị nào khác làm nó đồng thời.
Operation là một lớp trừu tượng đại diện cho một đơn vị công việc. Mỗi tác vụ có một trạng thái: ready, executing, finished hoặc cancelled. Các trạng thái tương thích với KVO (Quan sát Giá trị Khóa), cho phép phản ứng với các thay đổi — ví dụ: cập nhật giao diện khi một tác vụ hoàn thành. Operation tự động quản lý các cờ isExecuting và isFinished.
Theo Apple (2026), OperationQueue sử dụng GCD bên trong nhưng thêm các chức năng không có trong DispatchQueue: phụ thuộc, ưu tiên và hủy bỏ các tác vụ. Nếu ứng dụng vào chế độ nền, OperationQueue tạm dừng thực thi và tiếp tục khi quay lại. OperationQueue cũng tự động xem xét số lượng lõi CPU và chọn số luồng tối ưu.
Mỗi tác vụ trải qua bốn trạng thái: pending (đang chờ), ready (sẵn sàng), executing (đang thực thi) và finished (đã hoàn thành). Trạng thái cancelled có thể xảy ra ở bất kỳ giai đoạn nào trước khi hoàn thành. Các chuyển đổi trạng thái được theo dõi qua KVO — đây là nền tảng cho các cập nhật giao diện phản ứng. OperationQueue tự động xóa các tác vụ đã hoàn thành khỏi hàng đợi và thông báo cho các tác vụ phụ thuộc rằng điều kiện tiên quyết của chúng đã được đáp ứng, kích hoạt thực thi chúng.
Operation là một lớp trừu tượng yêu cầu ghi đè phương thức main() hoặc start(). Mã tác vụ được đặt trong phương thức main(), và các trạng thái isExecuting và isFinished được quản lý tự động. Đối với các tác vụ bất đồng bộ, cần ghi đè start() và quản lý thủ công các cờ trạng thái.
BlockOperation là một triển khai cụ thể của Operation thực thi một hoặc nhiều khối mã. BlockOperation trở thành đồng thời nếu bạn thêm nhiều khối qua addExecutionBlock(). Tác vụ chỉ hoàn thành sau khi tất cả các khối đã thêm được thực thi. BlockOperation là một lựa chọn thay thế thuận tiện cho các tác vụ đơn giản không cần kế thừa.
| Đặc điểm | Operation | BlockOperation |
|---|---|---|
| Loại Lớp | Trừu tượng | Cụ thể |
| Kế thừa | Cần thiết | Không cần thiết |
| Tính bất đồng bộ | Quản lý KVO thủ công | Tự động |
| Khối mã | Một trong main() | Một hoặc nhiều |
| Sử dụng | Tác vụ phức tạp với trạng thái | Tác vụ đơn giản một lần |
| Phù hợp cho | Phụ thuộc, hủy bỏ, tiến trình | Khối nhanh, hoàn thành |
| Bộ nhớ | Cao hơn do KVO và trạng thái | Tối thiểu, nhẹ |
Để tạo một tác vụ tùy chỉnh, kế thừa từ Operation và ghi đè main(). Bên trong, kiểm tra cờ isCancelled trước các tác vụ tốn kém để đảm bảo hủy bỏ nhanh chóng. Điều này rất quan trọng cho việc tải xuống tệp lớn hoặc xử lý dữ liệu hàng loạt. Lựa chọn giữa Operation và BlockOperation phụ thuộc vào độ phức tạp của tác vụ: cho các hành động đơn giản một lần, BlockOperation là đủ; cho logic có thể tái sử dụng với trạng thái, kế thừa từ Operation.
Phụ thuộc là lợi thế chính của OperationQueue so với DispatchQueue. Phương thức addDependency(_:) chỉ định rằng tác vụ B chỉ thực thi sau khi tác vụ A hoàn thành. Các phụ thuộc tạo thành một đồ thị có hướng không chu trình (DAG): nếu một phụ thuộc vòng tròn được thêm vào, hàng đợi bỏ qua nó và các tác vụ không được bắt đầu.
Ưu tiên của một tác vụ được đặt qua thuộc tính queuePriority với các giá trị: .veryLow, .low, .normal, .high, .veryHigh. Ưu tiên ảnh hưởng đến thứ tự bắt đầu giữa các tác vụ sẵn sàng nhưng không ghi đè phụ thuộc. OperationQueue đầu tiên giải quyết các phụ thuộc, sau đó áp dụng ưu tiên giữa các tác vụ có sẵn.
Một kịch bản điển hình — tải dữ liệu hồ sơ: đầu tiên tải người dùng, sau đó dựa trên id của họ tải bạn bè và bài viết. Thiết lập phụ thuộc giữa tải người dùng và tải bạn bè đảm bảo thứ tự chính xác mà không cần trình xử lý hoàn thành lồng nhau.
Thuộc tính maxConcurrentOperationCount giới hạn số lượng tác vụ thực thi đồng thời. Giá trị 1 tạo ra một hàng đợi tuần tự, giá trị mặc định (NSOperationQueueDefaultMaxConcurrentOperationCount) là tối ưu cho hệ thống và phụ thuộc vào tải hiện tại của thiết bị. Cấu hình phù hợp tham số này ngăn chặn tiêu thụ tài nguyên quá mức: để tải hình ảnh, 4–6 tác vụ đồng thời là đủ; cho các tác vụ tốn CPU, sử dụng số lõi xử lý.
Lựa chọn giữa OperationQueue và DispatchQueue phụ thuộc vào độ phức tạp của tác vụ. DispatchQueue là một công cụ nhẹ cho các cuộc gọi bất đồng bộ đơn giản. OperationQueue là một giải pháp nặng hơn cho các kịch bản phức tạp với nhiều tác vụ liên quan. Apple khuyên nên bắt đầu với DispatchQueue và chuyển sang OperationQueue chỉ khi cần phụ thuộc hoặc hủy bỏ. Đối với hầu hết các dự án iOS, sự kết hợp của cả hai công cụ mang lại sự cân bằng tối ưu giữa hiệu suất và linh hoạt.
Theo Ray Wenderlich (2025), trong các dự án iOS lớn, OperationQueue được sử dụng để tải nội dung với tiến trình và hủy bỏ, trong khi DispatchQueue được sử dụng cho tất cả các tác vụ bất đồng bộ khác. Tỷ lệ khoảng 20 trên 80 nghiêng về DispatchQueue.
Hãy xem ba ví dụ: một BlockOperation đơn giản, một Operation tùy chỉnh với phụ thuộc và một tác vụ có thể hủy để tải dữ liệu.
Trường hợp đơn giản nhất — thực thi một khối trên OperationQueue và xử lý kết quả qua completionBlock. Mỗi Operation có một thuộc tính completionBlock tích hợp, được gọi sau khi main() kết thúc.
let queue = OperationQueue()
let operation = BlockOperation()
operation.addExecutionBlock {
let data = NetworkService.fetchData()
OperationQueue.main.addOperation {
self.updateUI(data)
}
}
queue.addOperation(operation)
Phụ thuộc đảm bảo rằng parseOperation chỉ bắt đầu sau khi downloadOperation hoàn thành. Điều này loại bỏ nhu cầu về callback lồng nhau.
let download = BlockOperation { self.downloadJSON() }
let parse = BlockOperation { self.parseJSON() }
parse.addDependency(download)
let queue = OperationQueue()
queue.addOperations([download, parse], waitUntilFinished: false)
Ghi đè main() với kiểm tra isCancelled định kỳ. Điều này cho phép tác vụ dừng ngay lập tức khi bị hủy, mà không cần đợi một tác vụ tốn kém hoàn thành.
class ImageLoadOperation: Operation {
override func main() {
guard !self.isCancelled else { return }
let image = self.downloadImage()
guard !self.isCancelled else { return }
OperationQueue.main.addOperation { self.display(image) }
}
}
Hủy bỏ một tác vụ đặt cờ isCancelled thành true, nhưng không dừng phương thức main() đang chạy. Mã bên trong main() phải tự kiểm tra isCancelled và kết thúc khi cần thiết. Đây là quyết định kiến trúc của Apple — cho phép nhà phát triển giải phóng tài nguyên đúng cách khi hủy bỏ.
Quan sát KVO các thuộc tính isFinished và isExecuting cho phép phản ứng với việc hoàn thành tác vụ mà không cần callback rõ ràng. OperationQueue tự động xóa các tác vụ đã hoàn thành khỏi hàng đợi, nhưng chúng vẫn tồn tại trong bộ nhớ miễn là còn tham chiếu mạnh đến chúng. KVO là nền tảng để tích hợp OperationQueue với các framework phản ứng như RxSwift hoặc Combine.
Đăng ký isCancelled qua KVO cho phép cập nhật giao diện khi hủy bỏ tác vụ — ví dụ: hiển thị trình giữ chỗ thay vì tải xuống bị hủy. Thuộc tính isCancelled tương thích KVO, làm cho nó thuận tiện cho các pipeline phản ứng.
Không tạo số lượng lớn các tác vụ — mỗi Operation là một đối tượng riêng biệt trong bộ nhớ. Nếu một tác vụ ngắn và không yêu cầu phụ thuộc, hãy sử dụng DispatchQueue trực tiếp. OperationQueue phù hợp cho các kịch bản phức tạp với phụ thuộc rõ ràng, hủy bỏ và theo dõi tiến trình.
Kiểm tra isCancelled trước các tác vụ tốn kém bên trong phương thức main(). Trong trường hợp tải xuống tệp hoặc xử lý hình ảnh, kiểm tra sau mỗi bước quan trọng đảm bảo phản hồi nhanh khi hủy bỏ. Sử dụng if isCancelled { return } ở đầu main() và sau mỗi tác vụ chính.
Quản lý completionBlock đúng cách. Thuộc tính completionBlock của một tác vụ được gọi sau khi main() kết thúc, ngay cả khi tác vụ đã bị hủy. Kiểm tra isCancelled bên trong completionBlock để tránh cập nhật giao diện với dữ liệu sai. OperationQueue.main là một hàng đợi an toàn luồng cho các tác vụ giao diện, tương tự như DispatchQueue.main.
Tránh phụ thuộc vòng tròn — chúng khiến không có tác vụ nào trong vòng tròn được bắt đầu. OperationQueue không tự động phát hiện vòng tròn: nếu A phụ thuộc vào B và B phụ thuộc vào A, cả hai sẽ mãi mãi ở trạng thái ready. Lên kế hoạch đồ thị phụ thuộc trước.
Câu Hỏi Thường Gặp
OperationQueue được xây dựng trên GCD và thêm phụ thuộc, ưu tiên, KVO và hủy bỏ tác vụ. DispatchQueue là một công cụ nhẹ hơn cho các tác vụ bất đồng bộ đơn giản không có các khả năng này.
Đặt thuộc tính maxConcurrentOperationCount thành 1. Điều này biến OperationQueue thành một hàng đợi tuần tự trong khi vẫn giữ tất cả các lợi thế — phụ thuộc, ưu tiên và hủy bỏ.
Phương thức cancel() đặt cờ isCancelled nhưng không dừng phương thức main() đang thực thi. Mã tác vụ phải tự kiểm tra isCancelled và kết thúc. Hủy bỏ chỉ hoạt động cho các tác vụ đang chờ và sẵn sàng.
Bạn nên kế thừa Operation khi cần quản lý trạng thái, tính bất đồng bộ hoặc tái sử dụng logic. BlockOperation phù hợp cho các tác vụ đơn giản một lần không cần kế thừa.
Không, trừ khi bạn gọi waitUntilFinished với tham số true trên luồng chính. Các tác vụ thực thi trên luồng nền theo mặc định, và kết quả được trả về qua OperationQueue.main.
Tóm tắt
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.
Đọc thêm