代码之家  ›  专栏  ›  技术社区  ›  Shivam Tripathi

即使在设置了优先级和对操作的依赖性之后,操作队列也没有按顺序执行

  •  0
  • Shivam Tripathi  · 技术社区  · 6 年前

    我正在进行三个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
                }
            }
    
        }
    }
    
    0 回复  |  直到 6 年前
        1
  •  4
  •   Rob Md Fahim Faez Abir    6 年前

    有几个问题:

    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 直接的。作为 docs say :

      表示与单个任务关联的代码和数据的抽象类。...

      因为 操作 类是一个抽象类,您不直接使用它,而是使用子类或系统定义的子类之一( NSInvocationOperation BlockOperation )执行实际任务。

      因此使用 块状手术 或子类化,如下面第3点所示。

    2. 如果必须严格遵守订单,则不应使用优先级来管理操作执行的订单。作为 queuePriority docs say (重点补充):

      此值用于 影响 操作的出列和执行顺序。。。

      您应该仅在需要时使用优先级值来对非依赖操作的相对优先级进行分类。优先级值不应用于实现不同操作对象之间的依赖关系管理。如果需要在操作之间建立依赖关系,请使用 addDependency(_:) 方法代替。

      因此,如果将100个高优先级操作和100个默认优先级操作排队,则 保证所有高优先级的将在低优先级的开始运行之前启动。它倾向于优先考虑这些问题,但严格来说并非如此。

    3. 第一点是无意义的,因为您正在调用异步方法。所以你不能用简单的 操作 块状手术 . 如果不希望在前一个网络请求完成之前启动后续的网络请求,则需要将这些网络请求包装为自定义异步 操作 所有特殊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编码。而且你没有传递任何参数。

    推荐文章