设为首页 加入收藏

TOP

在Swift中应用Grand Central Dispatch(上)转载自的goldenfiredo001的博客(五)
2017-10-13 10:33:32 】 浏览:7636
Tags:Swift 应用 Grand Central Dispatch 转载 goldenfiredo001 博客
身就是顺序执行,屏障不会起到任何帮助作用。

  • 全局并发队列:慎用。其他系统可能也在使用队列,你不应该出于自身目的而独占队列。

  • 自定义并发队列:最佳选择。用于原子操作或是临界区代码。任何需要线程安全的设置和初始化都可以使用屏障。

  • 因为以上唯一合适的选择就是自定义并发队列,你需要生成一个这样的队列来处理屏障函数以隔离读写操作。并发队列允许多个线程同时的读操作。

    打开PhotoManager.swift并在photos属性下面添加如下私有属性到类中:

    1
    2
    private let concurrentPhotoQueue = dispatch_queue_create(
         "com.raywenderlich.GooglyPuff.photoQueue" , DISPATCH_QUEUE_CONCURRENT)

    使用dispatch_queue_create初始化一个并发队列concurrentPhotoQueue。第一个参数遵循反向DNS命名习惯;保证描述性以利于调试。第二个参数指出你的队列是顺序的还是并发的。

    注意:当在网上搜索例子时,你经常看到人们传0或NULL作为dispatch_queue_create的第二个参数。这是一种过时的方法来生成顺序调度队列;最好用参数显示声明。

    找到addPhoto并用如下实现替换之:

    1
    2
    3
    4
    5
    6
    7
    8
    func addPhoto(photo: Photo) {
       dispatch_barrier_async(concurrentPhotoQueue) {  // 1
         self._photos.append(photo)  // 2
         dispatch_async(GlobalMainQueue) {  // 3
           self.postContentAddedNotification()
         }
       }
    }

    来 看这段代码如何工作的: 1. 将写操作加入自定义的队列中。当临界区被执行时,这是队列中唯一一个在执行的任务。 2. 将对象加入数组。因为是屏障闭包,这个闭包不会和concurrentPhotoQueue中的其他任务同时执行。 3. 最终发送一个添加了图片的通知。这个通知应该在主线程中发送因为这涉及到UI,所以这里分派另一个异步任务到主队列中。

    这个任务解决了写问题,但是你还需要实现photos的读方法。

    为确保和写操作保持线程安全,你需要在concurrentPhotoQueue中执行读操作。但是你需要从函数返回读数据,所以不能异步地提交读操作到队列里,因为异步任务不能保证在函数返回前执行。

    因此,dispatch_sync是个极好的候选。

    dispatch_sync同步提交任务并等到任务完成后才返回。使用dispatch_sync和调度屏障一起来跟踪任务;或是在需要等待返回数据时使用dispatch_sync。

    仍 需小心。设想你调用dispatch_sync到当前队列中。这会造成死锁。因为调用在等待闭包完成,但是闭包无法完成(甚至根本没开始!),直到当前在 执行的任务结束,但当前任务没法结束(因为阻塞的闭包还没完成)!这就要求你必须清醒的认识到你从哪个队列调用了闭包,以及你将任务提交到哪个队列。

    概 述一下何时何地使用dispatch_sync: – 自定义顺序队列:非常小心;如果你在运行一个队列时调用dispatch_sync调度任务到同一个队列,你显然会制造死锁。 – 主队列(顺序):非常小心,原理同上。 – 并发队列:好选择。用在和调度屏障同步或是等待任务完成以继续后续处理。 还是在PhotoManager.swift中,替换photos如下:

    1
    2
    3
    4
    5
    6
    7
    var  photos: [Photo] {
       var  photosCopy: [Photo]!
       dispatch_sync(concurrentPhotoQueue) {  // 1
         photosCopy = self._photos  // 2
       }
       return  photosCopy
    }

    分别来看每个号码注释: 1. 同步调度到concurrentPhotoQueue队列执行读操作。 2. 保存图片数组的拷贝到photoCopy并返回它。

    恭喜 —— 你的PhotoManager单例已经是线程安全的了。不论你读或是写图片数组,你都有信心保证操作会安全的执行。

    回顾

    还是不能100%的确定GCD的本质?你可以自己创建使用GCD函数的简单例子,通过断点和NSLog来确保你明白发生了什么。

    我这里有两张动态GIF图片来帮助你理解dispatch_async和dispatch_sync。每张GIF上面都有代码辅助你理解;注意代码中的断点和相应的队列状态。

    重访dispatch_sync

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    override func viewDidLoad() {
       super .viewDidLoad()
      
       dispatch_sync(dispatch_get_global_queue(
           Int(QOS_CLASS_USER_INTERACTIVE.value), 0)) {
      
         NSLog( "First Log" )
      
       }
      
       NSLog( "Second Log" )
    }

    69.gif

    下面对图片中的几个状态做说明:
    1. 主队列按部就班的执行任务 —— 紧接着的任务是实例化包含viewDidL

    首页 上一页 2 3 4 5 6 下一页 尾页 5/6/6
    】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
    上一篇iOS开发代码规范(通用) 下一篇iOS动画

    最新文章

    热门文章

    Hot 文章

    Python

    C 语言

    C++基础

    大数据基础

    linux编程基础

    C/C++面试题目