Swift3.0 gcd学习(2)

3 篇文章 0 订阅

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:  0
workItem block:  0
block 2:  0
block 1:  1
workItem block:  1
block 2:  1
block 1:  2
workItem block:  2
block 2:  2
block 1:  3
workItem block:  3
block 2:  3
block 1:  4
workItem block:  4
block 2:  4
block 1:  5
workItem block:  5
block 2:  5

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

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

block 1:  0
block 1:  1
block 1:  2
block 1:  3
block 1:  4
block 1:  5
barrier workItem block:  0
barrier workItem block:  1
barrier workItem block:  2
barrier workItem block:  3
barrier workItem block:  4
barrier workItem block:  5
block 2:  0
block 2:  1
block 2:  2
block 2:  3
block 2:  4
block 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:  0
block 1:  0
block 2:  1
block 1:  1
block 2:  2
block 1:  2
block 2:  3
block 1:  3
block 2:  4
block 1:  4
block 2:  5
block 1:  5
block 3:  0
block 3:  1
block 3:  2
block 3:  3
block 3:  4
block 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 count
fatal error: UnsafeMutablePointer.deinitialize with negative count
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值