簡體   English   中英

如何確保OperationQueue中的操作一個接一個地完成

[英]How to assure that operations in an OperationQueue are finished one after another

執行相互依賴的OperationQueue ,可以使用OperationQueue確保它們以正確的順序執行。 但是,是否也可以確保操作一個接一個地完成?

讓我們假設一個異步執行的方法,需要一些時間才能完成:

public func performOperation(_ number: Int, success: @escaping (Int) -> Void)->Void {
        DispatchQueue(label: "operations").async {
            print("Operation #\(number) starts")
            usleep(useconds_t(1000-number*200)) // Block thread for some time
            success(number)
        }
}

操作和依賴項創建如下:

let operationQueue = OperationQueue.main
for operationNumber in 0..<4 { // Create operations as an example
    let operation = BlockOperation(block: {
        performOperation(operationNumber) { number in
            DispatchQueue.main.sync {
                print("Operation #\(number) finished")
            }
        }
    })
    operation.name = "Operation #\(operationNumber)"

    if operationNumber > 0 {
        operation.addDependency(operationQueue.operations.last!)
        // Print dependencies
        print("\(operation.name!) should finish after \(operation.dependencies.first!.name!)")
    }
    operationQueue.addOperation(operation)
}

使用以下輸出:

Operation #1 should finish after Operation #0
Operation #2 should finish after Operation #1
Operation #3 should finish after Operation #2
Operation #0 starts
Operation #1 starts
Operation #2 starts
Operation #3 starts
Operation #0 finished
Operation #3 finished
Operation #2 finished
Operation #1 finished

這顯然是不正確的。 似乎OperationQueue只確保操作以正確的順序啟動(而不是一個接一個地完成)。 盡管可以使用DispatchSemaphore執行此操作,但我想知道是否也可以使用OperationQueue

操作依賴關系正在完成,而不是啟動操作,因此系統的行為就像在那里記錄的那樣。 問題是DispatchQueue(label: "operations").async - 你的performOperation方法在你內部異步調度print …; usleep …; success …序列后立即退出print …; usleep …; success … print …; usleep …; success … print …; usleep …; success … ,為每個performOperation調用創建的新調度隊列。 然后,在Grand Central Dispatch管理的工作線程池的不同線程上執行該序列的打印/睡眠/成功回調。

我認為你在這里可能會感到困惑的是認為反復說明DispatchQueue(label: "operations")會使你獲得相同的串行調度隊列實例 - 事實並非如此,實際上你在每個調用時創建一個新的串行隊列。

BlockOperation ,也沒有理由在performOperation創建或調度到串行調度隊列,因為BlockOperation已經實現,以便在支持OperationQueue的GCD調度隊列上同時執行塊( 並發也可以限制 ) 。 在我的例子中,我要做的是使用OperationQueue()構造一個新的OperationQueue(而不是使用在主隊列上調度工作的OperationQueue.main ),然后異步地將成功回調調度到主隊列。

這個稍微修改過的例子向您展示了操作執行確實遵循了依賴關系(我沒有實現上面的OperationQueue相關建議,它可以說是你提出的問題的旁邊):

public func performOperation(_ number: Int, success: @escaping (Int) -> Void)->Void {
    print("Operation #\(number) starts")
    usleep(useconds_t(1000-(number*50))) // Block thread for some time
    success(number)
}

… 

let operationQueue = OperationQueue.main
for operationNumber in 0..<8 { // Create operations as an example
    let operation = BlockOperation(block: {
        self.performOperation(operationNumber) { number in
            print("Operation #\(number) finished")
        }
    })
    operation.name = "Operation #\(operationNumber)"

    if operationNumber > 0 {
        operation.addDependency(operationQueue.operations.last!)
        // Print dependencies
        print("\(operation.name!) should finish after \(operation.dependencies.first!.name!)")
    }
    operationQueue.addOperation(operation)
}

這將輸出......

Operation #1 should finish after Operation #0
Operation #2 should finish after Operation #1
Operation #3 should finish after Operation #2
Operation #4 should finish after Operation #3
Operation #5 should finish after Operation #4
Operation #6 should finish after Operation #5
Operation #7 should finish after Operation #6
Operation #0 starts
Operation #0 finished
Operation #1 starts
Operation #1 finished
Operation #2 starts
Operation #2 finished
Operation #3 starts
Operation #3 finished
Operation #4 starts
Operation #4 finished
Operation #5 starts
Operation #5 finished
Operation #6 starts
Operation #6 finished
Operation #7 starts
Operation #7 finished

請參閱此示例以優雅地了解BlockOperation 示例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM