Соблюдать количество операций в очереди URLSession?

я использую URLSessionнастроить таким образом:

      public struct NetworkSession {
    
    public var session: URLSession
    public let sessionOperationQueue = OperationQueue()
    
    public init() {
        
        let sessionConfiguration = URLSessionConfiguration.default
        sessionConfiguration.timeoutIntervalForRequest = 20
        sessionOperationQueue.maxConcurrentOperationCount = OperationQueue.defaultMaxConcurrentOperationCount
        sessionOperationQueue.qualityOfService = .userInitiated
        session = URLSession(configuration: sessionConfiguration, delegate: nil, delegateQueue:sessionOperationQueue)
            
    }

    .....

}

Я хотел бы наблюдать за количеством задач, найденных в очереди.

Я попытался использовать Combine:

          sessionOperationQueue.publisher(for: \.operationCount).sink { count in
        print("operations count: \(count)")
    }
    .store(in: &subscribers)

Но это только печатает 0при инициализации и никогда не обновляется по мере запуска и завершения запросов.

Как я могу контролировать количество задач, найденных в очереди?

1 ответ

тл;др

Наблюдение за количеством операций в очереди сеанса не приведет к тому, что вы хотите.

  • В традиционном коде очередь используется для отдельных методов делегата, а не для переноса всего запроса-ответа.
  • При использовании новой версии очередь операций вообще не используется (что спорно, учитывая предыдущее наблюдение).

В итоге, несмотря на то, что у него есть метод, чтобы узнать, какие ожидающие запросы выполняются, у него, насколько мне известно, нет наблюдаемого свойства для этого (если, конечно, вы не откажетесь от обработчиков завершения и не используете только представления делегата). Итак, если вы хотите динамически отслеживать количество ожидающих запросов, просто следите за этим самостоятельно. Шаблон асинхронного пользовательского подкласса кажется излишним (но он описан в раздел этого ответа ). Было бы проще просто направить все мои сетевые запросы через метод, который увеличивает счетчик по мере поступления запросов и уменьшает его по завершении.


Длинный ответ с примерами кода

Вы можете использовать KVO для наблюдения за изменениями очереди (см. ниже), но это не приведет к тому, чего вы хотите. Это не операция, обертывающая весь сетевой запрос и ответ, а скорее отдельные операции для отдельных обратных вызовов делегата сеанса и обработчика завершения.

Например, рассмотрим:

      class ViewController: UIViewController {

    lazy var session: URLSession = {
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 20
        return URLSession(configuration: configuration, delegate: nil, delegateQueue: queue)
    }()

    let queue: OperationQueue = {
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
        queue.qualityOfService = .userInitiated
        return queue
    }()

    var observer: NSKeyValueObservation?

    override func viewDidLoad() {
        super.viewDidLoad()

        observer = queue.observe(\.operationCount, options: .new) { queue, change in
            print("Observer reported operationCount =", change.newValue!)
        }

        for i in 1000..<1010 {
            let url = URL(string: "https://httpbin.org/get?value=\(i)")!
            session.dataTask(with: url) { data, _, error in
                guard let data = data else {
                    print(error ?? URLError(.badServerResponse))
                    return
                }

                print("Request", i, "returned", data.count, "bytes")
            }.resume()
        }
    }
}

Это производит:

      Observer reported operationCount = 1
Observer reported operationCount = 2
Observer reported operationCount = 3
Request 1000 returned 405 bytes
Observer reported operationCount = 4
Observer reported operationCount = 3
Request 1002 returned 405 bytes
Observer reported operationCount = 2
Request 1004 returned 405 bytes
Observer reported operationCount = 1
Request 1001 returned 405 bytes
Observer reported operationCount = 0
Observer reported operationCount = 1
Observer reported operationCount = 2
Request 1006 returned 405 bytes
Observer reported operationCount = 3
Observer reported operationCount = 2
Observer reported operationCount = 3
Request 1005 returned 405 bytes
Observer reported operationCount = 4
Observer reported operationCount = 3
Observer reported operationCount = 4
Request 1003 returned 405 bytes
Observer reported operationCount = 3
Request 1008 returned 405 bytes
Observer reported operationCount = 2
Request 1007 returned 405 bytes
Observer reported operationCount = 1
Request 1009 returned 405 bytes
Observer reported operationCount = 0

Обратите внимание: вы никогда не увидите подтверждение того, что есть десять ожидающих запросов. В сообщает, что находится в очереди делегатов, а это не то, что вы ищете.

Кстати, в приведенном выше очередь делегатов последовательная (как советовали в документации ). Сам факт того, что это последовательная очередь, позволяющая выполнять одновременные сетевые запросы, является еще одним свидетельством того, что существует не операция, обертывающая весь запрос, а скорее для отдельных обратных вызовов делегата.


Интересно, если вы используете новый - методов очередь операций вообще не используется. Это имеет смысл (учитывая, что используется новая система параллелизма), но на данный момент это не указано в документации. В любом случае, приведенное ниже не вызовет никаких изменений счетчика операций:

      func startRequests() async throws {
    try await withThrowingTaskGroup(of: Void.self) { group in
        for i in 0..<4 {
            let url = URL(string: "https://httpbin.org/get?value=\(i)")!
            group.addTask {
                let (data, _) = try await self.session.data(from: url)
                print("Request", i, "returned", data.count, "bytes")
            }
        }

        try await group.waitForAll()
    }
}

Но это спорно, учитывая, что очередь операций не достигает того, чего вы хотите, несмотря ни на что.

Другие вопросы по тегам