GCD理解

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值