为什么在递归函数调用中使用信号量时会出现死锁



为什么在递归函数调用中使用信号量时会出现死锁?

我需要做几个同步的HTTPGET请求,所以我写了以下函数:

func doSynchronousHTTPGetRequest(url: String, completionHandler: (NSData!, NSURLResponse!, NSError!) -> Void) {
    if let url = NSURL(string: url) {
        let semaphore = dispatch_semaphore_create(0);
        let task = NSURLSession.sharedSession().dataTaskWithURL(url) {
            (data, response, error) in
            dispatch_semaphore_signal(semaphore);
            completionHandler(data, response, error)
        }
        task.resume()
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }
}

当我这样称呼它时:

    self.doSynchronousHTTPGetRequest("https://2ch.hk/b/threads.json") {
        (data, response, error) in
        self.doSynchronousHTTPGetRequest("https://2ch.hk/b/threads.json") {
            (data, response, error) in
            // ...
        }
    }

它无限期地等待第二次等待。

为什么?我做错了什么?我该怎么修?

我不知道它为什么挂起,但修复方法是:

func doSynchronousHTTPGetRequest(url: String, completionHandler: (NSData!, NSURLResponse!, NSError!) -> Void) {
    if let url = NSURL(string: url) {
        let semaphore = dispatch_semaphore_create(0);
        var _data:NSData!
        var _response:NSURLResponse!
        var _error:NSError!
        let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
            // set the results to outer variables
            (_data, _response, _error) = (data, response, error)
            // then restart the main thread
            dispatch_semaphore_signal(semaphore);
        }
        task.resume()
        // Here is always main thread
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // wait until signalled...
        // Main thread restarted!
        // call callback closure from main thread.
        completionHandler(_data, _response, _error)
    }
}
doSynchronousHTTPGetRequest("https://2ch.hk/b/threads.json") {
    (data, response, error) in
    doSynchronousHTTPGetRequest("https://2ch.hk/b/threads.json") {
        (data, response, error) in
        // ...
    }
}

主要区别在于将调用哪个线程completionHandler

在我的代码中,它是主线程,但在OP代码中,这是另一个线程。


我想,我找到了原因:

    let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
        // print current queue's concurrent count
        println(NSOperationQueue.currentQueue()?.maxConcurrentOperationCount)
        dispatch_semaphore_signal(semaphore);
        completionHandler(data, response, error)
    }

这将打印1,意味着它是串行队列。看起来,NSURLSession对多个请求使用一个串行队列。

然后在第二次调用时,在串行队列中,

  1. 创建信号量
  2. 创建并CCD_ 4任务
  3. 用CCD_ 5锁定线程,直到信号量发出信号

这会导致死锁。这就是OP代码挂起的原因。

最新更新