iOS用NSCondition休眠线程

2020-05-03  本文已影响0人  呼呼兔

记得以前项目中有个需求是,部分接口的调用需要放到非主线程中,并且还有顺序要求。那时NSURLConnection还没废弃,用sendSynchronousRequestGCD实现的,大概如下。

    //创建个串行队列
    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()
}

注意

  1. 这两个方法,我没有放到项目中使用,所以不确定是否有bug。
  2. 使用NSCondition问题很多。
    • 由于broadcast可以唤醒所有被-wait方法阻塞着的线程。所以在其他地方调用broadcast方法会影响到这里的。
    • 根据苹果官方文档,-signal方法本身就不完全保证是准确的,会存在其他线程没有调用-signal方法,但是被wait的线程依然被唤醒的情况。
    • 就算被wait的线程的唤醒时机没有问题,但是在被wait的线程被唤醒到执行后面代码期间,程序状态可能会发生变化,这也是一个风险项。
  3. 由于这里使用了多线程。所以有必要的情况下一定用NSLock加锁解锁。

参考

上一篇 下一篇

猜你喜欢

热点阅读