本次同豪门聊聊 iOS 开发被的多线程处理, 以及资源保障, 就是 Lock
的定义。

大抵线程无处无以

先是,我们设询问一个基本概念。 以 APP
程序来说,几乎有的顺序仍然以差不多线程机制下运行的。 比如所有的 APP
都晤面爆发一个主线程,一般用来 UI 相关的处理。
假诺你你要请一些网络数据,你就用以其它一个线程中开展,
否则就是会面堵塞主线程的响应,结果就是用户界面显然的卡顿。
相信这些知识我们应该相比清楚了。

苟我辈于出顺序的下,很轻习惯给用单线程的考虑来进展付出。所以就为是咱本次要商量的内容,在多数情况下,这样的构思方法不会合出无限死之题材。
但一旦涉及到大半只线程访问与一个资源的时候,
就有或会晤发出一些料之外的题材了。

共享资源问题

来说点儿具体的, 假设大家的 APP 中生这么一个文本 data.json:

{
    "favorites": 20
}

得管其当呈现在 UI 下面的一个数, 比如某个摄像的访问数。
那么一旦想多是访问数,我们一般用通过一个异步线程来改是文件,
比如那样:

let queue1 = DispatchQueue(label: "operate favorite 1")
queue1.async {

    self.addFavorite(num: 1)
    print(self.readFavorite())

}

此 addFavorite 方法用来描写副文件。 readFavorite 用来读取文件被的情。
假诺我们这边文件中 favorites 的初阶值是 20 ,那么是异步操作完后,
print 语句就会见输出 21。 因为大家调用 addFavorite 方法把它加 1 了。

从即来拘禁,一切还称预期, 大家科学的将 favorites 的数值操作成了。

但来一个题目恐怕会师受不少人数所忽视。 这即使是 addFavorite 和 readFavorite
这片独道操作的公文实际上是一个共享资源。
其他的线程一样可读写这一个文件。大家再来拘禁一个例证:

let queue1 = DispatchQueue(label: "operate favorite 1")
queue1.async {

    self.addFavorite(num: 1)
    print(self.readFavorite())

}

let queue2 = DispatchQueue(label: "operate favorite 2")
queue2.async {

    self.addFavorite(num: 1)
    print(self.readFavorite())

}

本次我们开了少数只异步操作, 他们分别都调用 addFavorite 方法,
然后再调用 readFavorite
输出文件操作后的内容。那么这么的先后你会以为最后相会输出什么吗?

卿也许会合认为 queue1 先输出 21, 然后 queue2 再出口 22。 但实际上,
这段代码在生大概率下, 会输出两单 21。

呢足以知道呢,其中一个 addFavorite 的调用莫名其妙的丢失了。

那就是说由是呀也, 我们先从 addFavorite 函数的定义说从:

func addFavorite(num: Int) {

    do {

        var fileURL = try FileManager.default.url(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask, appropriateFor: nil, create: true)
        fileURL = fileURL.appendingPathComponent("data.json")

        let jsonData = try Data(contentsOf: fileURL)

        if var jsonObj = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: Any] {

            if let count = jsonObj["favorites"] as? NSNumber {

                jsonObj["favorites"] = NSNumber.init(value: count.intValue + num)

                let jsonData = try JSONSerialization.data(withJSONObject: jsonObj, options: JSONSerialization.WritingOptions.prettyPrinted)
                try jsonData.write(to: fileURL)

            }

        }

    } catch {

    }

}

这是 addFavorite 函数的完好代码, 它做的事务并无复杂, 先从 data.json
中赢得 favorites 的数值, 然后长 num, 再将结果写回文件。
假如未存并发操作, 这样的逻辑没有啊问题。
但假诺考虑到大半线程那些维度的言语, 就汇合出题目了。

遵照大家才代码中的 queue1, 它假使读取并形容副 data.json, 但很有或
queue2 这下也运行了, 它以 queue1 的写入操作没有水到渠成此前就是读取了
data.json。 这早晚, 他们少只读到的 favorites 数值都拿凡起值 20,
那么她们就是都会见拿 20 加上 1. 吧就算导致了一定量只出口都是 21 的结果了。

顿时吗是本身前为何说,在特别大概率下,会输出三只 21 的来头了。
线程的调度是由于操作系统来决定的,假设 queue2 调起的时刻, 正好 queue1
已经拿文件写副了了,这时便能博得正确的输出结果。 反之,要是 queue2
调起底时光 queue1 还尚未写副好,那么即便晤面现出出口八只同的 21 的情景了。
这一切都是由操作系统来支配。

至于多线程的再多基础背景知识,我们这边就然而基本上开展了,
这里让我们推荐一篇稿子
https://computing.llnl.gov/tutorials/pthreads
有趣味之心上人可研习一下。

怎么样解决

眼前说了这么多,
那么哪些解决那么些题材呢?其实业界前辈们已让我们提供了过多底缓解在了。
这里为我们介绍其中一个,就是 NSLock。 NSLock 是 iOS 提供给我们的一个 API
封装, 同时为是一个线程共享资源的通用解决方案 – 锁。 NSLock
就是对准线程加锁机制的一个包装,大家重来探视刚才的事例如何用 NSLock
来处理:

var lock = NSLock()

let queue1 = DispatchQueue(label: "operate favorite 1")
queue1.async {

    lock.lock()
    self.addFavorite(num: 1)
    lock.unlock()
    print(self.readFavorite())

}

let queue2 = DispatchQueue(label: "operate favirite 2")
queue2.async {

    lock.lock()
    self.addFavorite(num: 1)
    lock.unlock()
    print(self.readFavorite())

}

这一次我们对少独线程 addFavorite 调用的上下,都加上了 lock 和 unlock。
倘使还运行此程序,得出的结果就是咱预料的了。 那么 NSLock
到底做了呀啊,这半个线程在举行 addFavorite 那么些调用从前,
都待拿走之锁,但那锁在同一时间只可以被一个线程获取。
其它充裕没有收获成功之线程,就会吃操作系统挂于。
直到是锁给齐一个线程解锁(unlock)。

选举个有血有肉的例证来说, 比如,操作系统先调度到 queue1, 然后它们成通过
lock() 方法取得了是锁,起首念写文件之操作,当这一个操作都完成后,再调用
unlock 释放这一个锁。

这就是说只要以 queue1 还在读写文件是过程被, queue2
也叫调度了,并且实施了其的 lock 方法。 那上是因为 queue1
还以挤占是锁,操作系统就是会见被 queue2 暂时挂于, 直到 queue1 调用 unlock
将锁释放掉。 才能够被 queue2 继续执行。

json,如此这般一个机制, 就保证了同一时间只可以有一个线程来操作是文件,
也即便非会晤现出我们前面提到共享资源安全题材了。 同样, Lock
这些机制,也会带动一些特性损耗。 比如 queue2 会因为得不顶 lock
而给小挂于。 但对于相比关键的资源来说,这么些代价是值得付出的。

结尾

对于共享资源珍贵之考虑,应该好易会于世家忽略。
毕竟在大家平日支付中,这种资源撞之情景并无是总会来。
但即使暴发了,就肯定会是挺不便调试并发现的题材。
所以我们更要之是养成这样一个习惯,
在操作那些资源的时段,要考虑一下它会无相会于多独线程同时操作,在写代码的时节提前做好处理。

又多赏心悦目内容可关注微信公众号:
swift-cafe

相关文章

网站地图xml地图