Swift3.0 gcd学习(2)

来源:互联网 发布:战舰世界莫洛托夫数据 编辑:程序博客网 时间:2024/05/21 06:35

Swift3.0 gcd学习(2)

上一篇简单梳理了下gcd的基础概念和一些基本的使用方法。这一篇希望再深入研究下gcd的一些玩法,主要介绍在gcd里,怎样保证线程同步,有错误希望大家指正。
demo git地址

调度屏障barrier

有时会在一个并发的队列里读写一个数据对象,但如果对象并非线程安全,就会出现资源抢占的问题。之前使用dispatch_barrier_async来解决这个问题,在swift3.0,被搬到了DispatchWorkItem的flags属性中:

let workItemA = DispatchWorkItem(qos: DispatchQoS.default, flags: DispatchWorkItemFlags.barrier)        {            for i in 0...5            {                print("barrier workItem block: ", i);            }        }        let workItemB = DispatchWorkItem()            {                for i in 0...5                {                    print("workItem block: ", i);                }        }        let u = DispatchQueue(label: "com.justin.barrierAsync", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.workItem, target: nil);        u.async {            for i in 0...5            {                print("block 1: ", i);            }        }        u.async(execute: workItemA);        u.async {            for i in 0...5            {                print("block 2: ", i);            }        }

workItemB只做最普通的并发操作,输出结果如下:

block 1:  0workItem block:  0block 2:  0block 1:  1workItem block:  1block 2:  1block 1:  2workItem block:  2block 2:  2block 1:  3workItem block:  3block 2:  3block 1:  4workItem block:  4block 2:  4block 1:  5workItem block:  5block 2:  5

可以看出,三个block交错运行

下面我们换成workItemA,注意这个workItem的flag设置成了DispatchWorkItemFlags.barrier

block 1:  0block 1:  1block 1:  2block 1:  3block 1:  4block 1:  5barrier workItem block:  0barrier workItem block:  1barrier workItem block:  2barrier workItem block:  3barrier workItem block:  4barrier workItem block:  5block 2:  0block 2:  1block 2:  2block 2:  3block 2:  4block 2:  5

可以看出barrier确保提交的block是指定队列中,在特定时段唯一在执行的一个。只有在所有先于barrier的block都完成的情况下barrier block才开始执行,并且确保队列在此过程不会执行其它block。闭包完成后队列恢复。需要注意barrier只在自己创建的队列上有这种作用。

信号

另一种解决资源抢占问题的方法,就是使用信号。简单来说信号就是控制访问资源的数量,假设系统有10个资源,每个线程进入执行代码时占用一个资源,执行完成后,释放资源。当这10个线程的资源都没被释放时,第11个线程想要进入,就会被挡在外面。

声明一个信号:

let s = DispatchSemaphore(value: 2);

降低一个信号量:

s.wait();

增加一个信号量:

s.signal();
当信号量为0时,进程就会被阻塞,可以理解为资源被占完,其它线程想要入场,只能等待。一言不合上代码:

let s = DispatchSemaphore(value: 2);        let g = DispatchQueue.global();        g.async {            s.wait();            for i in 0...5            {                print("block 1: ", i);            }            s.signal();        }        g.async {            s.wait();            for i in 0...5            {                print("block 2: ", i);            }            s.signal();        }        g.async {            s.wait();            for i in 0...5            {                print("block 3: ", i);            }            s.signal();        }//输出结果block 2:  0block 1:  0block 2:  1block 1:  1block 2:  2block 1:  2block 2:  3block 1:  3block 2:  4block 1:  4block 2:  5block 1:  5block 3:  0block 3:  1block 3:  2block 3:  3block 3:  4block 3:  5

可以看出,因为信号对象声明时,只设置了2个信号量,所以当block1和block2进入执行代码后,block3就被挡在了外面,直到前面两个block把信号量释放出来后, block3才开始执行。
所以,在一些并发队列里处理一些非线程安全的数据时,可以这么干:

let g = DispatchQueue.global();        let s = DispatchSemaphore(value: 1);        var arr:[Int] = [];        for i in 0...100        {            g.async {                s.wait();//#1                arr.append(i);                s.signal();//#2            }        }

如果你把上面代码#1,#2注释掉,就会出现:

fatal error: UnsafeMutablePointer.deinitialize with negative countfatal error: UnsafeMutablePointer.deinitialize with negative count
0 0
原创粉丝点击