GCD理解

来源:互联网 发布:软件 会展 编辑:程序博客网 时间:2024/06/04 19:46

GCD

GCD (Grand Central Dispatch) 是 libdispatch 的市场名称,而 libdispatch 作为 Apple 的一个库,为并发代码在多核硬件上执行提供有力支持。
在iOS多线程编程中,GCD占有相当重要的地位。因为相对于 NSThread 和 NSOperation, GCD的使用更简单方便并且实现了真正的硬件多核支持,开发者要做的只是定义想执行的任务并追加到适当的 Dispatch Queue中。

dispatch_queue

dispatch_queue 是一个执行处理的等待队列,是一个FIFo(先进先出)队列,这样就保证了先放进队列的任务能够
先执行。

dispatch_queue 是线程安全的,在并发任务中被调用时不会产生任务问题。所以可以放心使用。

dispatch_queu类型:

1) 串行 dispatch_queue 在串行队列一次只执行一个任务,并且按照我们添加到队列的顺序来执行。

CSDN图标

在串行队列中,在同一个时间间隔内只有一个任务可以执行,当上一个任务结束后才会开始下一个任务。

2) 并发 dispatch_queue 在并发队列中一次可以执行多个,它们会按照被添加的顺序开始执行。

在并发队列中同一个时间间隔内可以有多个任务同时执行。

并发队列

在并发队列中的任务可以不用等待现在执行中的处理结束,这样就可以并行执行多个处理,但并行执行的处理数量
取决于当前系统状态。即iOS和OS X 基于dispatch Queue中的处理数、cpu核心以及CPU负荷等当前系统的状态
来绝定Concurrent Dispatch Queue中执行的处理数。

  • 并行:在同一时间内有多个任务执行
  • 并发:在同一时间间隔内有多个任务执行

可以简单的理解为并发是一种能力,可以允许多个任务执行。并行是真正的在同一时间内执行多个任务。所以要想可以并行
首先必须可以并发。但并发不要求并行。并行在多核中执行起来相对容易,可以给每个核分配一个线程执行各自的任务。
但在单核中执行并行就不那么容易了,在单核中必须使用时间片轮转法。例如,有两个线程Thread1,Thread2,在一个时间
片内先执行Thread1的任务,然后保留现场,在一下个时间片执行Thread2,当时间片结束时保留现场并切换到Thrad1开始
下一个时间片.由于这个时间片相当相当短,给我们的感觉就是在同时执行。

dispatch_queu 创建

  1. 通过CGD API
func dispatch_queue_create(_ label: UnsafePointer<Int8>, _ attr: dispatch_queue_attr_t!) -> dispatch_queue_t!

每次调用 dispatch_queue_create 都会创建一个新的dispatch_queue_t和一个新的线程,所以过多的创建
dispatch_queue 会创建过多的线程,就会消耗大量内存,引起大量的上下文切换。这样多个线程的queue就行成了一个
并行的任务队列,这时候如果同时去写一个数据就可能出问题了。

并行串行队列

2.获取系统中已存在的dispatch_queue

Main Dispatch Queue:在主线程中执行的串行dispatch_queue,因为主线程只有一个,所以Main Dispatch Queue也只存在一个。Main Dispatch 中的所有任务都是在主线程的runloop中执行的,跟NSObject的
performSelectOnMainThread一样。所以执行UI更新等需要在主线程中执行的任务必须添加到Main Dispatch
queue中.

Global Dispatch Queue:全局可用的并行队列。队列有4个执行的优先等级。High Priority, Default
Low, BackGround.

实战使用

  1. 在子线程执行耗时的操作避免阻塞界面,在主线程中更新界面
    dispatch_queue_t mainQueue = dispatch_get_main_queue();    dispatch_async(mainQueue, ^{         // 刷新UI    });    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);    dispatch_async(globalQueue, ^{        // 费时且不需要阻塞线程的操作    });
        dispatch_async(dispatch_get_global_queue(0, 0), { () -> Void in            let url:NSURL = NSURL(string:"http://7xlgnt.com1.z0.glb.clouddn.com/Serial.png")!            let imageData: NSData? = NSData(contentsOfURL:url)            dispatch_async(dispatch_get_main_queue(), { () -> Void in                if let imageData = imageData {                    imageView.image = UIImage(data: imageData)                }            })        })
  1. 延时操作 dispatch_after
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0)) { () -> Void in            print("come here")        }        dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_MSEC)), dispatch_get_global_queue(0, 0)) { () -> Void in            print("come here2")        }        dispatch_after(dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_USEC)), dispatch_get_global_queue(0, 0)) { () -> Void in                    print("come here3")                }

我们先看几个值

print(NSEC_PER_SEC) 结果: 1000000000 皮秒 1000000000 = 1秒

print(NSEC_PER_MSEC) 结果: 1000000 微妙 1000000 = 1 秒

print(NSEC_PER_USEC) 结果: 1000 毫秒 1000 毫秒 = 1秒

print(DISPATCH_TIME_NOW) 0

print(DISPATCH_TIME_FOREVER) 18446744073709551615

print(UInt64.max) 18446744073709551615

public func dispatch_time(when: dispatch_time_t, _ delta: Int64) -> dispatch_time_t

when 什么时候开始计算延迟,是一个Uint64的值, 这里的值都是皮秒

_delta 延迟多少皮秒执行。

要延迟1秒我们可以这样:

dispatch_time(DISPATCH_TIME_NOW,Int64(1 * NSEC_PER_SEC)
dispatch_time(DISPATCH_TIME_NOW,Int64(1000 * NSEC_PER_MSEC)
dispatch_time(DISPATCH_TIME_NOW,Int64(1000000 * NSEC_PER_USEC)

所以上面的代码执行结果是:

come here3

come here2

come here

  1. 保证只执行一次的 dispatch_once

public func dispatch_once(predicate: UnsafeMutablePointer, _ block: dispatch_block_t)

单例是我们在代码中最常见的只执行一次的例子。有如下一个类

class Car {    var name:String?    static var car:Car?    class func shareInstance() -> Car {        if (car == nil) {            car = Car()            print("开始初始化")        }        return car!    }}

上面的类中有一个shareInstance的方法来生成一个单例对象,但是上面的方法并不是线程安全的,如果在多线程中执行上述方法就会产问题

        dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in            var car = Car.shareInstance()        };        dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in            var car = Car.shareInstance()        };        dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in            var car = Car.shareInstance()        };

执行时我们可以看到输出了三次 “开始初始化”,这说明car == nil并不能有效的去判断当前是不是已经开始初始化了,因为当第
一个的初始化还未完成时第二个的初始已经开始了,然后第三个也开始初始化,所以说看到输出了三个“开始初始化”

class Car {    var name:String?    class var shareInstance:Car {        struct Static {            static var car:Car?            static var onceToken : dispatch_once_t = 0        }        dispatch_once(&Static.onceToken) { () -> Void in            Static.car = Car()        }        return Static.car!    }}

如果换成上面的代码使用dispatch_once,就会变成线程安全了。

1 0
原创粉丝点击