一组属性包装器和其他类型,专门设计用于在您的 Swift 项目中提供快速、简单且高效的线程安全。
选择 File
-> Swift Packages
-> Add Package Dependency
并输入 https://github.com/Flowduino/ThreadSafeSwift.git
您可以在您自己的 Package 的 Package.swift
文件中使用 ThreadSafeSwift
作为 Package Dependency。
let package = Package(
//...
dependencies: [
.package(
url: "https://github.com/Flowduino/ThreadSafeSwift.git",
.upToNextMajor(from: "1.1.0")
),
],
//...
)
从那里,在任何需要它的您的 package 的 target 中,将 ThreadSafeSwift
作为“target dependency”引用。
targets: [
.target(
name: "YourLibrary",
dependencies: [
"ThreadSafeSwift",
],
//...
),
//...
]
然后,您可以在任何需要它的代码中执行 import ThreadSafeSwift
。
以下是 ThreadSafeSwift
提供的功能的一些快速简便的用法示例
您可以使用 ThreadSafeSemaphore
属性包装器将任何值类型封装在线程安全的 DispatchSemaphore
之后。 对于大多数类型来说,这非常容易
@ThreadSafeSemaphore var myInt: Int
此外,您可以直接访问底层的 DispatchSemaphore
,这在您需要为必须原子地执行的多个操作获取锁时非常有用
@ThreadSafeSemaphore var myInts: [Int]
//...
func incrementEveryIntegerByOne() {
_myInts.lock.wait()
for (index,val) in myInts.enumerated() {
myInts[index] = val + 1
}
_myInts.lock.signal()
}
当然,对于数组,您应该尽量减少所需的 get/set 操作的数量,以及 DispatchSemaphore
被锁定的持续时间
@ThreadSafeSemaphore var myInts: [Int]
//...
func incrementEveryIntegerByOne() {
var values = myInts // This would marshal the `DispatchSemaphore` and return a copy of the Array, then release the `DispatchSemaphore`
for (index,val) in values.enumerated() {
myInts[index] = val + 1
}
myInts = values // This would marshal the `DispatchSempahore` and replace the entire Array with our modified one, then release the `DispatchSemaphore`
}
通常,需要在值上执行多个操作...当您需要执行此操作时,您需要确保在这些操作期间保持对该值的 DispatchSemaphore
锁。 为了方便这一点,我们可以对任何 ThreadSafeSemaphore
装饰的变量使用 withLock
方法
@ThreadSafeSemaphore var myInts: [Int] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
这里我们有一个 ThreadSafeSemaphore
装饰的整数数组。
如果我们想对该数组中的任意数量的值执行任意数量的操作,我们现在可以使用 withLock
以线程安全的方式执行此操作
func incrementEachValueByOne() {
_myInts.withLock { value in
for (index, val) in value.enumerated() {
value[index] = val + 1
}
}
}
请注意调用 withLock
方法时 myInts
之前的下划线 _
。 这很重要,因为下划线指示 Swift 引用 Property Decorator 而不是它的 wrappedValue
。
重要提示: - 您不能在闭包的范围内引用变量本身(在上面的例子中,是 myInts
)。 如果你这样做,线程会在该命令处锁定并且不再继续。 对该值的所有修改都必须针对闭包本身范围内定义的 value
执行(如上所示)。
因此,正如您所看到的,我们现在可以使用 @ThreadSafeSemaphore
装饰器封装复杂类型,并在 DispatchSemaphore
锁的安全性范围内对其所有成员进行操作。
与 ThreadSafeSemaphore.withLock
(如上所述)一样,我们可能需要在 DispatchSemaphore
的上下文中执行一个或多个操作,但仅当此时可以获得 DispatchSemaphore
锁时。 如果此时无法获得 DispatchSemaphore
锁,我们可能需要执行另一段条件代码。
我们可以很容易地做到这一点
@ThreadSafeSemaphore var myInts: [Int] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
同样,我们声明我们的 @ThreadSafeSemaphore
装饰的变量。
现在让我们看看我们将如何针对 myInts
使用 withTryLock
func incrementEachValueByOne() {
_myInts.withTryLock { value in
// If we got the Lock
for (index, val) in value.enumerated() {
value[index] = val + 1
}
} _: {
// If we couldn't get the Lock
print("We wanted to acquire the Lock, but couldn't... so we can do something else instead!")
}
}
重要提示: - 您不能在任何一个闭包的范围内引用变量本身(在上面的例子中,是 myInts
)。 如果你这样做,线程会在该命令处锁定并且不再继续。 对该值的所有修改都必须针对闭包本身范围内定义的 value
执行(如上所示)。
这些条件闭包非常有用,当您的代码需要根据它是否可以在执行点获取 DispatchSemaphore
锁来沿着不同的执行路径前进时。
提示: - 我使用这种方法来实现 Collections 的“旋转门锁”。 这项功能很快就会添加到这个库中!
ThreadSafeSwift
在 MIT 许可证下可用。 有关更多信息,请参见 LICENSE file。