记得以前项目中有个需求是,部分接口的调用需要放到非主线程中,并且还有顺序要求。那时NSURLConnection
还没废弃,用sendSynchronousRequest
和GCD
实现的,大概如下。
//创建个串行队列
dispatch_queue_t queue = dispatch_queue_create("xxxxx", DISPATCH_QUEUE_SERIAL);
//接口1 加入队列中
dispatch_async(queue, ^{
NSData *data = [NSURLConnection sendSynchronousRequest:xxx returningResponse:xx error:xx];
}];
//接口2 加入队列中
dispatch_async(queue, ^{
NSData *data = [NSURLConnection sendSynchronousRequest:xxx returningResponse:xx error:xx];
}];
...
但是NSURLConnection
换成URLSession
后,URLSession
中的数据返回都是异步的block和代理,没有同步请求。所以就想到了线程先休眠,等到回调后在停止休眠,返回data。一查使用NSCondition
还真能实现这种效果。实现如下。
func testUrl() -> Data? {
var reData: Data?
let cond = NSCondition()
cond.lock()
URLSession.shared.dataTask(with: URL.init(string: "https://www.baidu.com")!) { (data, resp, error) in
reData = data
cond.signal()
}.resume()
cond.wait()
cond.unlock()
return reData
}
//默认是串行队列
let queue = DispatchQueue.init(label: "jj", attributes: [])
queu.async {
let data1 = testUrl()
}
queu.async {
let data2 = testUrl()
}
//这样就能按着顺序调用接口,处理数据。
其实用DispatchGroup
也能实现类似的需求。
func testUrl11() -> Data? {
var reData: Data?
let group = DispatchGroup()
group.enter()
URLSession.shared.dataTask(with: URL.init(string: "https://www.baidu.com")!) { (data, resp, error) in
reData = data
group.leave()
}.resume()
group.wait()
return reData
}
queu.async {
let data3 = testUrl11()
}
queu.async {
let data4 = testUrl11()
}
注意
- 这两个方法,我没有放到项目中使用,所以不确定是否有bug。
- 使用
NSCondition
问题很多。- 由于
broadcast
可以唤醒所有被-wait
方法阻塞着的线程。所以在其他地方调用broadcast
方法会影响到这里的。 - 根据苹果官方文档,
-signal
方法本身就不完全保证是准确的,会存在其他线程没有调用-signal
方法,但是被wait
的线程依然被唤醒的情况。 - 就算被
wait
的线程的唤醒时机没有问题,但是在被wait
的线程被唤醒到执行后面代码期间,程序状态可能会发生变化,这也是一个风险项。
- 由于
- 由于这里使用了多线程。所以有必要的情况下一定用
NSLock
加锁解锁。
参考
- 【多线程之】NSCondition分析,凯奇1992,2019.01.18 16:31:26
网友评论