iOS URL 请求。信号量问题
iOS URL Requests. Semaphore issues
我进入并发编程时遇到了一些信号量问题。
我的功能首先从服务器加载数据,分析接收到的信息,然后在必要时向服务器发出第二次请求。
我尝试了不同的方法 运行,其中 none 做得很好。
我目前的代码对我来说似乎是正确的,但在第二次请求时它只是锁定(可能像死锁)并且最后一个日志是“<__NSCFLocalDataTask:0x7ff470c58c90>{taskIdentifier:2}{暂停}”
请告诉我我不知道什么。也许有更优雅的方式来完成这些目的?
提前致谢!
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。我为什么要这样做。服务器 returns 有限的数据计数。要获得更多,我必须使用偏移量。
这是死锁,因为您正在等待 URLSession
的 delegateQueue
上的信号量。默认委托队列不是主队列,而是一个串行后台队列(即 OperationQueue
和 maxConcurrentOperationCount
的 1
)。因此,您的代码正在等待同一个串行队列上的信号量,该队列应该向信号量发送信号。
战术修复是确保您没有在会话的完成处理程序 运行 所在的同一串行队列上调用 wait
。有两个明显的修复:
不要使用 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)
或者,您可以通过将带有信号量的代码分派到其他队列来解决此问题,例如
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()
为了完整起见,我会指出您可能根本不应该使用信号量来发出这些请求,因为您最终会因为发出一系列请求而付出 material 性能损失连续请求(加上你阻塞了一个线程,这通常是不鼓励的)。
为做到这一点而对这段代码进行的重构更加可观。它基本上需要发出一系列并发请求,也许使用 "download" 任务而不是 "data" 任务来最小化内存影响,然后当所有请求完成后,最后根据需要将它们拼凑在一起(由 Operation
"completion" 操作或调度组通知触发)。
我进入并发编程时遇到了一些信号量问题。 我的功能首先从服务器加载数据,分析接收到的信息,然后在必要时向服务器发出第二次请求。
我尝试了不同的方法 运行,其中 none 做得很好。 我目前的代码对我来说似乎是正确的,但在第二次请求时它只是锁定(可能像死锁)并且最后一个日志是“<__NSCFLocalDataTask:0x7ff470c58c90>{taskIdentifier:2}{暂停}”
请告诉我我不知道什么。也许有更优雅的方式来完成这些目的?
提前致谢!
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。我为什么要这样做。服务器 returns 有限的数据计数。要获得更多,我必须使用偏移量。
这是死锁,因为您正在等待 URLSession
的 delegateQueue
上的信号量。默认委托队列不是主队列,而是一个串行后台队列(即 OperationQueue
和 maxConcurrentOperationCount
的 1
)。因此,您的代码正在等待同一个串行队列上的信号量,该队列应该向信号量发送信号。
战术修复是确保您没有在会话的完成处理程序 运行 所在的同一串行队列上调用 wait
。有两个明显的修复:
不要使用
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)
或者,您可以通过将带有信号量的代码分派到其他队列来解决此问题,例如
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()
为了完整起见,我会指出您可能根本不应该使用信号量来发出这些请求,因为您最终会因为发出一系列请求而付出 material 性能损失连续请求(加上你阻塞了一个线程,这通常是不鼓励的)。
为做到这一点而对这段代码进行的重构更加可观。它基本上需要发出一系列并发请求,也许使用 "download" 任务而不是 "data" 任务来最小化内存影响,然后当所有请求完成后,最后根据需要将它们拼凑在一起(由 Operation
"completion" 操作或调度组通知触发)。