Как зациклить синхронно в swift - PullRequest
1 голос
/ 25 марта 2019

Я пытаюсь написать алгоритм для сортировки списка, и я использую сетевой вызов (запрос API) к API карт Google для получения информации о расстоянии между двумя точками в списке.

Я использую цикл while и перебираю список, пока размер списка не станет 0.

На каждой итерации я выполняю сетевой вызов, а после ответа я что-то удаляю из списка.

Я пытался использовать семафоры с кодом ниже, и он не работает, как ожидалось.

let semaphore = DispatchSemaphore(value: 1)
let dispatchQueue = DispatchQueue(label: "taskQueue")

dispatchQueue.async {
  while unvistedPoints.count > 0{
    print("The size of the list is ", unvisited.count)
    self.findNextVistablePoint(visited: visitedPoints, unvisted: unvistedPoints, completion: { (pointToVisit) in
      let indexofPointToVisit = unvistedPoints.firstIndex(where: {$0 === pointToVisit})
      unvistedPoints.remove(at: indexofPointToVisit!)
      visitedPoints.append(pointToVisit)
      semaphore.signal()
    })
  semaphore.wait()
}

В заявлении на печать должно быть напечатано 6,5,4,3,2,1.

Ответы [ 2 ]

0 голосов
/ 25 марта 2019

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

import UIKit
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

class SomeAsyncClass {

    var unvistedPoints = [ 6,5,4,3,2,1 ]
    let dispatchQueue = DispatchQueue(label: "taskQueue") // serial queue
    let semaphore = DispatchSemaphore(value: 1)

    public func doAsyncStuff() {
        for point in self.unvistedPoints {
            print("Queuing point \(point)")
            dispatchQueue.async {
                // block before sending the network request
                self.semaphore.wait()
                self.makeFakeNetworkRequest(point, completion: {
                    // request complete
                    print("Completed \(point)")
                    self.semaphore.signal()
                })
            }
        }
    }

    func makeFakeNetworkRequest(_ point:Int, completion:()->()) {
        let interval = TimeInterval(exactly: (arc4random() % 3) + 1)!
        print("Point \(point): Sleeping for: \(interval)")
        Thread.sleep(forTimeInterval: interval)
        print("Point \(point): Awoken after: \(interval)")
        completion()
    }
}

var c = SomeAsyncClass()
c.doAsyncStuff()

Вот вывод:

Queuing point 6
Queuing point 5
Queuing point 4
Point 6: Sleeping for: 3.0
Queuing point 3
Queuing point 2
Queuing point 1
Point 6: Awoken after: 3.0
Completed 6
Point 5: Sleeping for: 3.0
Point 5: Awoken after: 3.0
Completed 5
Point 4: Sleeping for: 3.0
Point 4: Awoken after: 3.0
Completed 4
Point 3: Sleeping for: 3.0
Point 3: Awoken after: 3.0
Completed 3
Point 2: Sleeping for: 3.0
Point 2: Awoken after: 3.0
Completed 2
Point 1: Sleeping for: 3.0
Point 1: Awoken after: 3.0
Completed 1

С учетом сказанного, это не лучший способ сделать это. Лучше использовать конструкцию iOS, предназначенную для этой цели, которая называется OperationQueue - она ​​имеет гранулярные элементы управления параллелизмом (maxConcurrentOperationCount) и может использоваться в качестве основы для URLSession (delegateQueue). Я бы рекомендовал использовать эту конструкцию, если она соответствует вашим потребностям.

0 голосов
/ 25 марта 2019

Ожидание должно идти перед запросом сети. Кроме того, на самом деле нет никакой причины использовать асинхронность dispatchQueue здесь, потому что ваш цикл выполняет очень мало работы (сетевые запросы уже асинхронны), и я не вижу смысла в использовании цикла while и изменения массива, когда вы можете просто использовать вместо цикла. Вот пример:

import PlaygroundSupport
import UIKit

struct StarwarsCharacter: Codable {
    let name: String
}

enum APIResult<T> {
    case failure(Error), success(T)
}

func getCharactersSerially(completion: @escaping (APIResult<StarwarsCharacter>) -> ()) {
    var characters: [StarwarsCharacter] = []
    let semaphore = DispatchSemaphore(value: 1)
    let urls = (1...9).map {"https://swapi.co/api/people/\($0)"}.compactMap(URL.init(string:))
    urls.forEach { url in
        semaphore.wait()
        print("starting request for \(url) at \(Date())")
        URLSession.shared.dataTask(with: url) { data, response, error in
            print("completed request for \(url) at \(Date())")
            defer {
                semaphore.signal()
            }
            guard error == nil,
                let data = data,
                let character = try? JSONDecoder().decode(StarwarsCharacter.self, from: data) else {
                    completion(.failure(error ?? NSError()))
                    return
            }
            completion(.success(character))
            }.resume()
    }
}

PlaygroundPage.current.needsIndefiniteExecution = true
getCharactersSerially() { result in
    switch result {
    case .failure(let error):
        print(error.localizedDescription)
    case .success(let character):
        print(character.name)
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...