为什么在递归函数调用中使用信号量时会出现死锁?
我需要做几个同步的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
对多个请求使用一个串行队列。
然后在第二次调用时,在串行队列中,
- 创建信号量
- 创建并CCD_ 4任务
- 用CCD_ 5锁定线程,直到信号量发出信号
这会导致死锁。这就是OP代码挂起的原因。