ThreadSafeSwift

一组属性包装器和其他类型,专门设计用于在您的 Swift 项目中提供快速、简单且高效的线程安全。

安装

Xcode 项目

选择 File -> Swift Packages -> Add Package Dependency 并输入 https://github.com/Flowduino/ThreadSafeSwift.git

Swift Package Manager 项目

您可以在您自己的 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 - 属性包装器

您可以使用 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`
}

ThreadSafeSemaphore.withLock - 执行闭包,同时保持锁

通常,需要在值上执行多个操作...当您需要执行此操作时,您需要确保在这些操作期间保持对该值的 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.withTryLock - 执行闭包,同时保持锁(如果我们能获取到锁),否则执行失败闭包

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