Multithreading 对类中变量的线程安全访问
在一个可能有多个线程运行的应用程序中,为了安全起见,我不确定这些方法是否可以在多线程环境下访问,我已经做了一个测试类来演示这种情况 一种方法Multithreading 对类中变量的线程安全访问,multithreading,swift,Multithreading,Swift,在一个可能有多个线程运行的应用程序中,为了安全起见,我不确定这些方法是否可以在多线程环境下访问,我已经做了一个测试类来演示这种情况 一种方法已经被编程为线程安全(如果做得对,也请评论),但其他方法没有 在这种情况下,remove和add中只有一行代码,是否有必要使它们成为线程安全的,还是会被夸大 import Foundation class Some {} class Test { var dict = [String: Some]() func has(key: Str
已经
被编程为线程安全(如果做得对,也请评论),但其他方法没有
在这种情况下,remove
和add
中只有一行代码,是否有必要使它们成为线程安全的,还是会被夸大
import Foundation
class Some {}
class Test {
var dict = [String: Some]()
func has(key: String) -> Bool {
var has = false
dispatch_sync(dispatch_queue_create("has", nil), { [unowned self] in
has = self.dict[key] != nil
})
return has
}
func remove(key: String) -> Some {
var ob = dict[key]
dict[key] = nil
return ob
}
func add(key: String, ob: Some) {
dict[key] = ob
}
}
评论后编辑
class Some {}
class Test {
var dict = [String: Some]()
private let queue: dispatch_queue_t = dispatch_queue_create("has", DISPATCH_QUEUE_CONCURRENT)
func has(key: String) -> Bool {
var has = false
dispatch_sync(queue) {
has = self.dict[key] != nil
}
return has
}
func remove(key: String) -> Some? { //returns
var removed: Some?
dispatch_barrier_sync(queue) {
removed = self.dict.removeValueForKey(key)
}
return removed
}
func add(key: String, ob: Some) { //not async
dispatch_barrier_sync(queue) {
self.dict[key] = ob
}
}
}
检查密钥是否存在的方式不正确。您每次都在创建一个新队列,这意味着操作不是同步进行的 我会这样做:
class Some {}
class Test {
var dict = [String: Some]()
private let queue: dispatch_queue_t = dispatch_queue_create("has", DISPATCH_QUEUE_CONCURRENT)
func has(key: String) -> Bool {
var has = false
dispatch_sync(queue) { [weak self] in
guard let strongSelf = self else { return }
has = strongSelf.dict[key] != nil
}
return has
}
func remove(key: String) {
dispatch_barrier_async(queue) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = nil
}
}
func add(key: String, ob: Some) {
dispatch_barrier_async(queue) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = ob
}
}
}
首先,我将创建一个串行队列,该队列将作为对象的属性用于访问字典,而不是每次都创建一个新队列。队列是私有的,因为它只在内部使用
当我想从类中获取一个值时,我只是同步地向队列分配一个块,并等待块完成,然后返回队列是否存在。因为这不会改变字典,所以在并发队列上运行这种类型的多个块是安全的
当我想从字典中添加或删除值时,我将块添加到队列中,但带有一个屏障。这将在队列运行时停止队列上的所有其他块。完成后,所有其他块都可以并发运行。我使用的是异步调度,因为我不需要等待返回值
假设您有多个线程试图查看键值是否存在或添加或删除值。如果您有很多读取,那么它们会同时发生,但是当运行其中一个将更改字典的块时,所有其他块将等待此更改完成,然后再次开始运行
通过这种方式,您可以在获取值时快速方便地并发运行,并且在字典发生变化时阻塞线程是安全的
编辑以添加
self
在块中标记为weak
,这样就不会创建参考循环。正如评论中提到的@MartinR;对象可能在块仍在队列中时被解除分配,如果发生这种情况,则self未定义,并且您可能会在尝试访问字典时遇到运行时错误,因为它也可能被解除分配
通过将块内的声明self
设置为弱,如果对象存在,则self
将不会为零,并且可以有条件地展开为strongSelf
,指向self
,并创建强引用,这样在执行块中的指令时,self
将不会被解除分配。当这些说明完成后,strongSelf
将超出范围,并释放对self的强引用
这有时被称为“强自我,弱自我舞蹈”
再次编辑:Swift 3版本
class Some {}
class Test {
var dict = [String: Some]()
private let queue = DispatchQueue(label: "has", qos: .default, attributes: .concurrent)
func has(key: String) -> Bool {
var has = false
queue.sync { [weak self] in
guard let strongSelf = self else { return }
has = strongSelf.dict[key] != nil
}
return has
}
func remove(key: String) {
queue.async(flags: .barrier) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = nil
}
}
func add(key: String, ob: Some) {
queue.async(flags: .barrier) { [weak self] in
guard let strongSelf = self else { return }
strongSelf.dict[key] = ob
}
}
}
这里是另一个swift 3解决方案,它提供对任何对象的线程安全访问 如果需要,它将分配与“对象”关联的递归pthread_互斥体
class LatencyManager
{
private var latencies = [String : TimeInterval]()
func set(hostName: String, latency: TimeInterval) {
synchronizedBlock(lockedObject: latencies as AnyObject) { [weak self] in
self?.latencies[hostName] = latency
}
}
/// Provides thread-safe access to given object
private func synchronizedBlock(lockedObject: AnyObject, block: () -> Void) {
objc_sync_enter(lockedObject)
block()
objc_sync_exit(lockedObject)
}
}
然后您可以调用例如set(主机名:“stackoverflow.com”,延迟:1)
更新
您只需在swift文件(而不是类)中定义方法即可:
然后像这样使用它:
synchronizedAccess(to: myObject) {
myObject.foo()
}
回答得好!但有一个问题是:
[弱自我]
会不会更安全?执行屏障块时,Test
对象可能会被释放。@MartinR在这种情况下,块运行的队列不也会被释放吗?从:“队列由系统保留,直到块运行完成。”–因此(我认为)即使对象被解除分配并释放对队列的引用,队列也将继续运行,直到所有未完成的块都完成为止。@Abizern如果我还有另一个字典,比如说dict2
,我是否应该为dict2
s声明另一个dispatch\u queue\t
变量,删除
和添加
functions@user2727195如果您有另一个字典,那么使用不同的队列是一个好主意。阻止一个字典的getter是没有意义的,因为您正在变异另一个字典。
synchronizedAccess(to: myObject) {
myObject.foo()
}