• 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后的执行没有任何影响。

  • 相关阅读:
    2015-01-21
    水文分析手册ArcHydro Tool 中文操作手册
    03018_监听器Listener
    元旦去峨眉山吧,人间值得
    常用Oracle SQL集锦
    结合公司现状浅谈CMDB
    CentOS7-Nginx编译安装
    Linux配置C++11编译环境
    Python实现通用web框架
    Python实现通用web框架
  • 原文地址:https://www.cnblogs.com/breezemist/p/4388564.html
Copyright © 2020-2023  润新知