簡體   English   中英

即使在設置優先級和依賴於操作之后,操作隊列也不按順序執行

[英]Operation Queue not executing in order even after setting priority and dependency on Operations

我正在進行三次api調用,並且希望API1首先執行,一旦完成API2應該執行,然后執行API3。 我使用操作隊列來增加對操作的依賴性。 我嘗試設置優先級,但沒有按順序獲取api調用。 幫助我如何正確地做到這一點。

代碼是這樣的:

let op1 = Operation()
op1.completionBlock = {
    self.APICall(urlString: self.url1)
}
op1.queuePriority = .veryHigh

let op2 = Operation()
op2.completionBlock = {
    self.APICall(urlString: self.url2)
}
op2.queuePriority = .high

let op3 = Operation()
op3.completionBlock = {
    self.APICall(urlString: self.url3)
}

op3.queuePriority = .normal

op2.addDependency(op1)
op3.addDependency(op2)

queue.addOperations([op1, op2, op3], waitUntilFinished: false)

我把API調用方法放在DispatchQueue.main.sync中,如下所示:

func APICall(urlString: String) {

    let headers: HTTPHeaders = [
        "Accept": "text/html"
    ]
    print(urlString)
    DispatchQueue.main.sync {

        Alamofire.request(urlString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers).responseJSON {
            response in
            // self.stopActivityIndicator()
            print(response.result.value)
            switch response.result {
            case .success:
                break
            case .failure(let error):
                break
            }
        }

    }
}

有幾個問題:

  1. 如果您嘗試管理操作之間的依賴關系,則不能將操作的completionBlock用於依賴項依賴的代碼。 直到操作完成后才調用完成塊(從而破壞了任何依賴的目的)。

    所以以下內容不會按預期工作:

     let queue = OperationQueue() let op1 = Operation() op1.completionBlock = { print("starting op1") Thread.sleep(forTimeInterval: 1) print("finishing op1") } let op2 = Operation() op2.completionBlock = { print("starting op2") Thread.sleep(forTimeInterval: 1) print("finishing op2") } op2.addDependency(op1) queue.addOperations([op1, op2], waitUntilFinished: false) 

    但是如果你定義這樣的操作,它將起作用:

     let op1 = BlockOperation() { print("starting op1") Thread.sleep(forTimeInterval: 1) print("finishing op1") } let op2 = BlockOperation { print("starting op2") Thread.sleep(forTimeInterval: 1) print("finishing op2") } 

    (但這只能起作用,因為我重新定義了同步操作。請參見下面的第3點。)

    值得注意的是,您通常不會直接使用Operation 正如文檔所說

    一個抽象類,表示與單個任務關聯的代碼和數據。 ...

    因為Operation類是一個抽象類,所以不要直接使用它,而是使用系統定義的子類( NSInvocationOperationBlockOperation )子類或使用其中一個來執行實際任務。

    因此,使用BlockOperation ,上面或BlockOperation它,如下面第3點所示。

  2. 如果必須嚴格遵守訂單,則不應使用優先級來管理操作執行的訂單。 正如queuePriority 文檔所說 (強調添加):

    該值用於影響操作出列和執行的順序......

    您應該僅在需要時使用優先級值來對非依賴操作的相對優先級進行分類。 不應使用優先級值來實現不同操作對象之間的依賴關系管理。 如果需要在操作之間建立依賴關系,請改用addDependency(_:)方法。

    因此,如果您排隊100個高優先級操作和100個默認優先級操作,則無法保證所有高優先級操作都將在優先級較低的操作開始運行之前啟動。 它傾向於優先考慮它們,但嚴格來說並非如此。

  3. 第一點是沒有實際意義,因為您正在調用異步方法。 所以你不能使用簡單的OperationBlockOperation 如果您不希望后續網絡請求在前一個網絡請求完成之前啟動,則您需要將這些網絡請求包裝在自定義異步Operation子類中,並使用所需的所有特殊KVO:

     class NetworkOperation: AsynchronousOperation { var request: DataRequest static var sessionManager: SessionManager = { let manager = Alamofire.SessionManager(configuration: .default) manager.startRequestsImmediately = false return manager }() init(urlString: String, parameters: [String: String]? = nil, completion: @escaping (Result<Any>) -> Void) { let headers: HTTPHeaders = [ "Accept": "text/html" ] let string = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! let url = URL(string: string)! request = NetworkOperation.sessionManager.request(url, parameters: parameters, headers: headers) super.init() request.responseJSON { [weak self] response in completion(response.result) self?.finish() } } override func main() { request.resume() } override func cancel() { request.cancel() } } 

    然后你可以這樣做:

     let queue = OperationQueue() let op1 = NetworkOperation(urlString: ...) { result in ... } let op2 = NetworkOperation(urlString: ...) { result in ... } let op3 = NetworkOperation(urlString: ...) { result in ... } op2.addDependency(op1) op3.addDependency(op2) queue.addOperations([op1, op2, op3], waitUntilFinished: false) 

    因為那是使用AsynchronousOperation子類(如下所示),所以在異步請求完成之前,操作不會完成。

     /// Asynchronous operation base class /// /// This is abstract to class performs all of the necessary KVN of `isFinished` and /// `isExecuting` for a concurrent `Operation` subclass. You can subclass this and /// implement asynchronous operations. All you must do is: /// /// - override `main()` with the tasks that initiate the asynchronous task; /// /// - call `completeOperation()` function when the asynchronous task is done; /// /// - optionally, periodically check `self.cancelled` status, performing any clean-up /// necessary and then ensuring that `finish()` is called; or /// override `cancel` method, calling `super.cancel()` and then cleaning-up /// and ensuring `finish()` is called. public class AsynchronousOperation: Operation { /// State for this operation. @objc private enum OperationState: Int { case ready case executing case finished } /// Concurrent queue for synchronizing access to `state`. private let stateQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".rw.state", attributes: .concurrent) /// Private backing stored property for `state`. private var _state: OperationState = .ready /// The state of the operation @objc private dynamic var state: OperationState { get { stateQueue.sync { _state } } set { stateQueue.sync(flags: .barrier) { _state = newValue } } } // MARK: - Various `Operation` properties open override var isReady: Bool { return state == .ready && super.isReady } public final override var isAsynchronous: Bool { return true } public final override var isExecuting: Bool { return state == .executing } public final override var isFinished: Bool { return state == .finished } // KVN for dependent properties open override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> { if ["isReady", "isFinished", "isExecuting"].contains(key) { return [#keyPath(state)] } return super.keyPathsForValuesAffectingValue(forKey: key) } // Start public final override func start() { if isCancelled { state = .finished return } state = .executing main() } /// Subclasses must implement this to perform their work and they must not call `super`. The default implementation of this function throws an exception. open override func main() { fatalError("Subclasses must implement `main`.") } /// Call this function to finish an operation that is currently executing public final func finish() { if !isFinished { state = .finished } } } 
  4. 作為非常小的觀察,您的代碼使用JSON參數指定了GET請求。 這沒有意義。 GET請求沒有可以包含JSON的主體。 GET請求僅使用URL編碼。 除此之外你沒有傳遞任何參數。

暫無
暫無

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

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