iOS gcd dispatch使用注意,dispatch_syn可能产生的死锁

 

我们在使用dispatch_sync 时可能会出现死锁,看下面的例子:

import UIKit

class ViewController: UIViewController {
    var serialQueue:dispatch_queue_t!
    var currentQueue:dispatch_queue_t!

    override func viewDidLoad() {
        super.viewDidLoad()

        serialQueue = dispatch_queue_create("com.kings.dboperation", DISPATCH_QUEUE_SERIAL)
        currentQueue = dispatch_queue_create("com.kings.dboperation", DISPATCH_QUEUE_CONCURRENT)

        printCurrentThread("main")
      
        dispatch_async(serialQueue, {()->Void in
            sleep(3)
            self.printCurrentThread("111111111")

       //阻塞当前操作,并向serialQueue发出block,等待serialQueue执行完毕这个block,才会解除阻塞,也就是要求提交的block先于外部block完成。
       //但是我们用的是serialQueue,这个queue要求先加入的block必须先完成,也就是要求外部block要先于内部block完成。
       //这就产生了冲突。导致内部的block无法执行,代码锁死在了这里。
       //这里可以看出,
dispatch_sync即使在新线程中执行也会由于不小心而产生死锁。
            dispatch_sync(self.serialQueue, {()->Void in
                self.printCurrentThread("22222222222")
            })
            self.printCurrentThread("444444")
        })
    }

    func printCurrentThread(prefix:String){
        println(prefix + "   current thread is \(NSThread.currentThread())")
    }
}

输出如下

main   current thread is <NSThread: 0x7bf40950>{number = 1, name = main}
111111111   current thread is <NSThread: 0x7c0fa170>{number = 2, name = (null)}

程序没有输出2222 和4444的log。详细的解释请看代码中的注视部分。

我们继续试验,换下queue的类型,更改代码如下

dispatch_async(currentQueue, {()->Void in
            sleep(3)
            self.printCurrentThread("111111111")

            dispatch_sync(self.currentQueue, {()->Void in
                self.printCurrentThread("22222222222")
            })
            self.printCurrentThread("444444")

        })

下面是输出结果

main   current thread is <NSThread: 0x7b7596b0>{number = 1, name = main}
111111111   current thread is <NSThread: 0x7c0298b0>{number = 2, name = (null)}
22222222222   current thread is <NSThread: 0x7c0298b0>{number = 2, name = (null)}
444444   current thread is <NSThread: 0x7c0298b0>{number = 2, name = (null)}

我们看到,死锁消除了,主要是因为current queue 没有“先放入的block必须先结束” 这个约束!

我们可以看出,在任何时刻都不能在一个向serial queue提交的block中再次利用 dispath_sync向这个serial queue提交block,不然就会产生死锁!


 

再看把源代码相应部分替换为下面这2段代码,它们都在主线程中执行,第一段,死锁代码,

 dispatch_sync(serialQueue, {()->Void in
            sleep(3)
            self.printCurrentThread("111111111")
        
       //参考我们的上面的结论,这里没有向serialQueue继续提交,而是使用了main queue,竟然也死锁了!
dispatch_sync(dispatch_get_main_queue(), {()
->Void in self.printCurrentThread("22222222222") }) self.printCurrentThread("444444") })

第二段,不死锁代码

dispatch_async(serialQueue, {()->Void in
            sleep(3)
            self.printCurrentThread("111111111")

            dispatch_sync(dispatch_get_main_queue(), {()->Void in
                self.printCurrentThread("22222222222")
            })

            self.printCurrentThread("444444")

        })

由这2段代码的不同效果,我又尝试了以下代码

    override func viewDidLoad() {
        super.viewDidLoad()
dispatch_sync(dispatch_get_main_queue(), {()->Void in self.printCurrentThread("22222222222") }) }

结果是死锁。

我觉得可以这样理解,viewDidLoad这样的运行在主线程的函数,也是通过GCD机制运行的。viewDidLoad本身就是一个通过sync方法运行在main queue里的代码块,在这个代码块里在用dispatch_sync发送block到同一个queue(这里是main queue),就会产生死锁!上面第二段不死锁的原因就是dispatch_async可以让viewDidload这个代码块不被阻塞,也就不会导致和内部的

dispatch_sync(dispatch_get_main_queue(), {()->Void in
                self.printCurrentThread("22222222222")
            })

这段代码块产生资源竞争了!

 


 

问:dispatch_async 一定会建立新线程运行代码,一定不会在main thread中执行吗?

答:目前的测试结果看是这样的。

 

主线程中调用 + dispatch_async +  Serial Queue           在新线程中执行

主线程中调用 + dispatch_async + Cuncurrent Queue  在新线程中执行

  

问:dispatch_sync 会建立新线程吗?

答:从我们上面的测试看,dispatch_sync的代码块就执行在dispatch_sync调用时的线程里,具体结论还需要大量测试。

 

主线程中调用 + dispatch_sync +  Serial Queue  :在主线程中执行

主线程中调用 + dispatch_sync + Cuncurrent Queue  在主线程中执行

 

Serial Queue 保证的是加入到其中的代码块按顺序执行,并不指定block运行的具体线程。

另外async 和 sync都是针对是否阻塞当前代码块来说的,对block加入queue后的执行没有任何影响。

 

posted @ 2015-04-02 22:12  幻化成疯  阅读(619)  评论(0编辑  收藏  举报