为什么在递归函数调用中使用信号量时会出现死锁?
我需要做几个同步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
// ...
}
}
它在第二次等待时无限期等待。
为什么呢?我究竟做错了什么?我该如何解决?
答案 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
使用一个串行队列来处理多个请求。
然后在第二次调用时,在该串行队列中,
resume()
任务。dispatch_semaphore_wait
锁定线程,直到信号量发出信号。这会导致死锁。这就是OP代码挂起的原因。