iOS网址请求。信号量问题

时间:2016-06-19 11:03:23

标签: ios swift concurrency semaphore

我正在使用一些信号量问题进入并发编程。 我的函数首先从服务器加载数据,分析收到的信息,然后在必要时向服务器发出第二个请求。

我尝试了不同的方法让它运行,但没有一个能做得好。 我当前的代码FOR ME似乎是正确的,但在第二次请求时它只是锁定(可能像死锁),最后一个日志是"< __ NSCFLocalDataTask:0x7ff470c58c90> {taskIdentifier:2} {suspended}"

请告诉我,我不知道什么。也许有更优雅的方式来完成这些目的的完成?

提前谢谢!

var users = [Int]()
let linkURL = URL.init(string: "https://bla bla")
let session = URLSession.shared()
let semaphore = DispatchSemaphore.init(value: 0)
let dataRequest = session.dataTask(with:linkURL!) { (data, response, error) in
    let json = JSON (data: data!)
    if (json["queue"]["numbers"].intValue>999) {
        for i in 0...999 {
            users.append(json["queue"]["values"][i].intValue)
        }
        for i in 1...lround(json["queue"]["numbers"].doubleValue/1000) {
            let session2 = URLSession.shared()
            let semaphore2 = DispatchSemaphore.init(value: 0)
            let linkURL = URL.init(string: "https://bla bla")
            let dataRequest2 = session2.dataTask(with:linkURL!) { (data, response, error) in
                let json = JSON (data: data!)
                print(i)
                semaphore2.signal()
            }
            dataRequest2.resume()
            semaphore2.wait(timeout: DispatchTime.distantFuture)
        }
    }
    semaphore.signal()
}
dataRequest.resume()
semaphore.wait(timeout: DispatchTime.distantFuture)

P.S。我为什么要这样做。服务器返回有限的数据计数。为了获得更多,我必须使用偏移量。

1 个答案:

答案 0 :(得分:3)

这是死锁,因为您正在等待URLSession delegateQueue上的信号量。默认委托队列不是主队列,但它是一个串行后台队列(即OperationQueue maxConcurrentOperationCount 1)。所以你的代码正在等待同一个串行队列上的信号量,该队列应该用信号通知信号量。

战术修复是确保您没有在运行会话的完成处理程序的同一串行队列上调用wait。有两个明显的修复:

  1. 不要使用shared会话(其delegateQueue是一个串行队列),而是实例化您自己的URLSession并将其delegateQueue指定为并发您创建的OperationQueue

    let queue = OperationQueue()
    queue.name = "com.domain.app.networkqueue"
    
    let configuration = URLSessionConfiguration.default()
    let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: queue)
    
  2. 或者,您可以通过将带有信号量的代码分配给其他队列来解决此问题,例如

    let mainRequest = session.dataTask(with: mainUrl) { data, response, error in
        // ...
        DispatchQueue.global(attributes: .qosUserInitiated).async {
            let semaphore = DispatchSemaphore(value: 0)
    
            for i in 1 ... n {
                let childUrl = URL(string: "https://blabla/\(i)")!
                let childRequest = session.dataTask(with: childUrl) { data, response, error in
                    // ...
                    semaphore.signal()
                }
                childRequest.resume()
                _ = semaphore.wait(timeout: .distantFuture)
            }
        }
    }
    mainRequest.resume()
    
  3. 为了完整起见,我会注意到您可能根本不应该使用信号量来发出这些请求,因为您最终会因发出一系列连续请求而遭受重大性能损失(加上您'阻止线程,这通常是气馁的。)

    这个代码的重构是为了做到这一点。它基本上需要发出一系列并发请求,可能使用“下载”任务而不是“数据”任务来最小化内存影响,然后当所有请求完成后,根据需要将它们全部拼凑在一起(由任一请求触发) Operation“完成”操作或调度组通知。