Operation (P2)

by Hoang Anh Tuan
491 views

Ở phần 1 của bài viết, mình đã giới thiệu về Operation là gì. Ở phần 2 của loạt bài về Operation, mình sẽ nói về Dependency trong Operation.

Nội dung bài viết:

  • Operation Dependencies
  • Passing Data using Dependencies

Operation Dependencies:

Operation cho phép bạn thiết lập các sự phụ thuộc lẫn nhau. Điều này mang lại 2 lợi ích:

  • Giả sử operation 2 phụ thuộc vào operation 1. Khi đó operation 2 chỉ được thực hiện sau khi operation 1 đã hoàn thành.
  • Cung cấp 1 cách để bạn truyền data giữa các operation.
class DownloadImage: Operation {
    var index: Int
    
    init(ind: Int) {
        self.index = ind
    }

    override func main() {
        // Download image task
        print("Start downloading task \(index) at time: \(Date().timeIntervalSince1970)")
        sleep(5)
        print("Finish downloading task \(index) at time: \(Date().timeIntervalSince1970)")
    }
}

let firstOperation = DownloadImage(ind: 1)
let secondOperation = DownloadImage(ind: 2)
//firstOperation.addDependency(secondOperation)

let operationQueue = OperationQueue()
operationQueue.addOperation(firstOperation)
operationQueue.addOperation(secondOperation)

Ở trên là đoạn code khởi tạo 2 operation bình thường, giữa chúng chưa có dependency. Chạy đoạn code trên và đây là kết quả thu được:

2 task chạy song song

Giờ thì bỏ comment dòng code firstOperation.addDependency(secondOperation) và chạy thử:

Task 1 phụ thuộc vào task 2. Vì vậy, task 1 chỉ chạy khi task 2 đã hoàn thành.

Note: Bạn có thể tạo dependency cho 2 opeartion đang chạy ở 2 operation queue khác nhau.

Đây là 1 cách khá ngắn trong khi ở GCD bạn phải khai báo 1 dispatchGroup, sau đó gọi hàm enter(), leave(), notify(), … Tuy nhiên, cách làm này rất dễ gây ra deadlock.

Để remove 1 dependency, bạn chỉ cần gọi:

firstOperation.removeDependency(op: secondOperation)
Task 5 chỉ được chạy khi task 2 hoàn thành, task 2 chỉ chạy khi task 3 hoàn thành, task 3 chỉ chạy khi task 5 hoàn thành.

Ở đoạn code mẫu ở trên, nếu bạn sửa đoạn code thành như dưới đây thì code của bạn sẽ bị deadlock và không chạy.

firstOperation.addDependency(secondOperation)
secondOperation.addDependency(firstOperation)

Note: Hãy vẽ sơ đồ ra 1 tờ giấy để luôn clear về flow của bạn, tránh bị deadlock.

Truyền data giữa các operation thông qua dependency:

class Calculate: Operation {
    let firstNum: Int
    let secondNum: Int
    var sum: Int?
    
    init(first: Int, second: Int) {
        self.firstNum = first
        self.secondNum = second
    }
    
    override func main() {
        sum = firstNum + secondNum
    }
}

class Display: Operation {
    
    override func main() {
        // 2
        let sum = dependencies.compactMap{ ($0 as? Calculate)?.sum }.first
        
        guard let unwrappedSum = sum else {
            return
        }
        // 3
        print("Sum = \(unwrappedSum)")
    }
}

let calculateOperation = Calculate(first: 5, second: 10)
let displayOperation = Display()
// 1
displayOperation.addDependency(calculateOperation)


let operationQueue = OperationQueue()
// 4
operationQueue.addOperation(calculateOperation)
operationQueue.addOperation(displayOperation)
  1. Khởi tạo 2 operation, và set dependency để task Display chỉ chạy sau khi task Calculate hoàn thành.
  2. Hàm main() là hãm sẽ chạy khi 1 operation được chạy.
    Ở đây, ta sẽ lấy ra list operations có dependency với DisplayOperation, chọn ra operation nào là Calculate và lấy ra sum.
  3. Nếu sum khác nil thì hiển thị ra.
  4. Add các operation vào queue để chạy.

Kết quả hiển thị trên màn hình Console:

Dependency trong Operation là 1 trong những thứ giúp Operation vượt trội hơn so với GCD.

Ở phần tiếp, mình sẽ nói về Async Operation và xử lí cancel Opeartion.

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.