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

时间:2014-11-07 12:40:50

标签: multithreading swift

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

我需要做几个同步HTTP GET请求,所以我编写了以下函数:

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
            // ...
        }
    }

它在第二次等待时无限期等待。

为什么呢?我究竟做错了什么?我该如何解决?

1 个答案:

答案 0 :(得分:0)

我不知道它为什么会挂起,但解决方法是:

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. 创建并resume()任务。
  3. dispatch_semaphore_wait锁定线程,直到信号量发出信号。
  4. 这会导致死锁。这就是OP代码挂起的原因。