Ale*_*ohn 8 thread-safety swift ios-multithreading
在这种情况下我需要某种显式同步吗?
class A {
let val: Int;
init(_ newVal: Int) {
val = newVal
}
}
public class B {
var a: A? = nil
public func setA() { a = A(0) }
public func hasA() -> Bool { return a != nil }
}
Run Code Online (Sandbox Code Playgroud)
B类中还有另一种方法:
public func resetA() {
guard hasA() else { return }
a = A(1)
}
Run Code Online (Sandbox Code Playgroud)
setA()并且resetA()可以从任何线程以任何顺序调用。
我知道可能存在竞争条件,如果同时一个线程调用setA()和另一个线程调用resetA(),则结果无法确定:val要么是0,要么是1,但我不在乎:无论如何,hasA()将返回 true,won'它吗?
如果A是struct而不是class,答案会改变吗?
简而言之,不,属性访问器不是原子的。请参阅 WWDC 2016 视频ConcurrentProgramming With GCD in Swift 3,其中讨论了该语言中原生原子/同步的缺失。(这是一个 GCD 演讲,因此当他们随后深入研究同步方法时,他们会重点关注 GCD 方法,但任何同步方法都可以。)Apple 在自己的代码中使用了各种不同的同步方法。例如,ThreadSafeArrayStore他们使用他们使用NSLock)。
如果与锁同步,我可能会建议如下扩展:
\nextension NSLocking {\n func synchronized<T>(block: () throws -> T) rethrows -> T {\n lock()\n defer { unlock() }\n return try block()\n }\n}\nRun Code Online (Sandbox Code Playgroud)\nApple 在自己的代码中使用了这种模式,尽管他们碰巧称其withLock为synchronized. 但模式是一样的。
然后你可以这样做:
\npublic class B {\n private var lock = NSLock()\n private var a: A? // make this private to prevent unsynchronized direct access to this property\n\n public func setA() {\n lock.synchronized {\n a = A(0)\n }\n }\n\n public func hasA() -> Bool {\n lock.synchronized {\n a != nil\n }\n }\n\n public func resetA() {\n lock.synchronized {\n guard a != nil else { return }\n a = A(1)\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n也许
\npublic class B {\n private var lock = NSLock()\n private var _a: A?\n\n public var a: A? {\n get { lock.synchronized { _a } }\n set { lock.synchronized { _a = newValue } }\n }\n\n public var hasA: Bool {\n lock.synchronized { _a != nil }\n }\n\n public func resetA() {\n lock.synchronized {\n guard _a != nil else { return }\n _a = A(1)\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n我承认暴露 时有些不安hasA,因为它实际上邀请应用程序开发人员编写如下内容:
if !b.hasA {\n b.a = ...\n}\nRun Code Online (Sandbox Code Playgroud)\n就防止同时访问内存而言,这很好,但如果两个线程同时执行此操作,则它会引入逻辑竞争,其中两个线程都碰巧通过了测试!hasA,并且它们都替换了该值,最后一个获胜。
相反,我可能会编写一个方法来为我们执行此操作:
\npublic class B {\n private var lock = NSLock() // replacing os_unfair_lock_s()\n private var _a: A? = nil // fixed, thanks to Rob\n\n var a: A? {\n get { lock.synchronized { _a } }\n set { lock.synchronized { _a = newValue } }\n }\n\n public func withA(block: (inout A?) throws -> T) rethrows -> T {\n try lock.synchronized {\n try block(&_a)\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n这样你就可以这样做:
\npublic class B {\n private var lock = NSLock() // replacing os_unfair_lock_s()\n private var _a: A? = nil // fixed, thanks to Rob\n\n var a: A? {\n get { lock.synchronized { _a } }\n set { lock.synchronized { _a = newValue } }\n }\n\n public func withA(block: (inout A?) throws -> T) rethrows -> T {\n try lock.synchronized {\n try block(&_a)\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n这是线程安全的,因为我们让调用者将所有逻辑任务(检查是否a为nil,如果是,则初始化a)全部包装在一个同步步骤中。这是该问题的一个很好的通用解决方案。它可以防止逻辑竞争。
上面的例子太抽象了,很难理解。那么让我们考虑一个实际的例子,Apple\xe2\x80\x99s 的变体ThreadSafeArrayStore:
b.withA { a in\n if a == nil {\n a = ...\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n这里我们有一个同步数组,我们在其中定义一个接口以线程安全的方式与底层数组交互。
\n或者,如果您想要一个更简单的示例,请考虑使用线程安全对象来跟踪最高的项目是什么。我们不会有hasValue布尔值,而是将其合并到我们的同步updateIfTaller方法中:
public class ThreadSafeArrayStore<Value> {\n private var underlying: [Value]\n private let lock = NSLock()\n\n public init(_ seed: [Value] = []) {\n underlying = seed\n }\n\n public subscript(index: Int) -> Value {\n get { lock.synchronized { underlying[index] } }\n set { lock.synchronized { underlying[index] = newValue } }\n }\n\n public func get() -> [Value] {\n lock.synchronized {\n underlying\n }\n }\n\n public func clear() {\n lock.synchronized {\n underlying = []\n }\n }\n\n public func append(_ item: Value) {\n lock.synchronized {\n underlying.append(item)\n }\n }\n\n public var count: Int {\n lock.synchronized {\n underlying.count\n }\n }\n\n public var isEmpty: Bool {\n lock.synchronized {\n underlying.isEmpty\n }\n }\n\n public func map<NewValue>(_ transform: (Value) throws -> NewValue) rethrows -> [NewValue] {\n try lock.synchronized {\n try underlying.map(transform)\n }\n }\n\n public func compactMap<NewValue>(_ transform: (Value) throws -> NewValue?) rethrows -> [NewValue] {\n try lock.synchronized {\n try underlying.compactMap(transform)\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n仅举几个例子。希望它能说明这个想法。
\n| 归档时间: |
|
| 查看次数: |
201 次 |
| 最近记录: |