我查阅了Swift书籍,但未能找到@synchronized的Swift版本。在Swift中如何实现互斥?
您可以使用GCD。它比@synchronized
更繁琐,但可以作为替代品:
let serialQueue = DispatchQueue(label: "com.test.mySerialQueue")
serialQueue.sync {
// code
}
synchronized
更慢,但是为什么会导致死锁?@TomKraina @bio @t0rst - Bill我自己曾经也在寻找这个功能,但得出结论:目前Swift内部还没有原生的构造函数能够实现这一点。
不过,基于一些Matt Bridges和其他人编写的代码,我编写了这个小的辅助函数。
func synced(_ lock: Any, closure: () -> ()) {
objc_sync_enter(lock)
closure()
objc_sync_exit(lock)
}
使用方法非常简单明了
synced(self) {
println("This is a synchronized closure")
}
关于这个问题,我发现了一个问题。在这里将数组作为锁参数传递似乎会导致一个非常晦涩的编译器错误。不过除此之外,它似乎按预期工作。
Bitcast requires both operands to be pointer or neither
%26 = bitcast i64 %25 to %objc_object*, !dbg !378
LLVM ERROR: Broken function found, compilation aborted!
@synchronized
块的语法,但请注意,它与 Objective-C 中的真正内置块语句 @synchronized
并不完全相同,因为 return
和 break
语句不能像普通语句那样跳出包围函数/循环。 - newacctdefer
关键字的好地方,以确保即使 closure
抛出异常,也会调用 objc_sync_exit
。 - devios1objc_sync_xxx
的一个陷阱:https://straypixels.net/swift-dictionary-locking/ - Mike Taverne@synchronized
的东西时,我更喜欢使用在swift 2中引入的defer
语句。{
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
//
// code of critical section goes here
//
} // <-- lock released when this block is exited
这种方法的好处是,你的关键部分可以以任何所需的方式退出包含块(例如return
、break
、continue
、throw
),而“在延迟语句中的语句将被执行,无论程序控制如何转移。”1
lock
是什么?lock
如何初始化? - Van Du Tranlock
是任何 Objective-C 对象。 - ɲeuroburɳdo { obj_sync_enter(lock); defer { obj_sync_exit(lock); }; ...code... }
实现了与 @synchronized{ ...code... }
相同的效果。 - Gibezynu Nu你可以使用 objc_sync_enter(obj: AnyObject?)
和 objc_sync_exit(obj: AnyObject?)
将语句夹在中间。@synchronized 关键字在幕后使用这些方法。即:
objc_sync_enter(self)
... synchronized code ...
objc_sync_exit(self)
objc_sync_enter
和objc_sync_exit
是在Objc-sync.h中定义的方法,而且是开源的:http://opensource.apple.com/source/objc4/objc4-371.2/runtime/objc-sync.h - bontoJRobjc_sync_enter(…)
和objc_sync_exit(…)
是由iOS/macOS等API提供的公共头文件_(看起来它们在路径usr/include/objc/objc-sync.h
中的….sdk
内)。找出某个东西是否为公共API最简单的方法是(在Xcode中)输入函数名称(例如objc_sync_enter()
;对于C函数,不需要指定参数),然后尝试使用命令单击它。如果它向您显示该API的头文件,则说明您做得很好(因为如果它不是公共的,您将无法看到头文件)_。 - Slipp D. ThompsonObjective-C中@synchronized
指令的类比可以具有任意返回类型,并在Swift中具有良好的rethrows
行为。
// Swift 3
func synchronized<T>(_ lock: AnyObject, _ body: () throws -> T) rethrows -> T {
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
return try body()
}
defer
语句可以直接返回一个值,而不需要引入临时变量。
@noescape
属性到闭包中以允许更多的优化:// Swift 2
func synchronized<T>(lock: AnyObject, @noescape _ body: () throws -> T) rethrows -> T {
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
return try body()
}
SWIFT 4
在Swift 4中,你可以使用GCD的调度队列来锁定资源。
class MyObject {
private var internalState: Int = 0
private let internalQueue: DispatchQueue = DispatchQueue(label:"LockingQueue") // Serial by default
var state: Int {
get {
return internalQueue.sync { internalState }
}
set (newState) {
internalQueue.sync { internalState = newState }
}
}
}
.serial
似乎不可用。但是.concurrent
是可用的。 :/ - Travis GriggsmyObject.state = myObject.state + 1
,它将不会计算总操作次数,而是产生一个不确定的值。为了解决这个问题,调用代码应该被包装在一个串行队列中,以便读取和写入都是原子性的。当然,Obj-c 的 @synchronized
也有同样的问题,因此从这个意义上说,您的实现是正确的。 - BerikmyObject.state += 1
是一个读取和写入操作的组合。其他线程仍然可以在其中设置/写入值。根据 https://www.objc.io/blog/2018/12/18/atomic-variables/,更容易在同步块/闭包中运行 set
而不是在变量本身下运行。 - CyberMew在现代的Swift 5中,具备返回能力:
/**
Makes sure no other thread reenters the closure before the one running has not returned
*/
@discardableResult
public func synchronized<T>(_ lock: AnyObject, closure:() -> T) -> T {
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
return closure()
}
使用以下方式来利用返回值的功能:
let returnedValue = synchronized(self) {
// Your code here
return yourCode()
}
synchronized(self) {
// Your code here
yourCode()
}
objc_sync_enter
一样。我更喜欢将lock
参数隐藏在私有的let或iVar中,而不是使用self
,除非它需要发布以允许其他人进行同步。这是一个非常罕见的情况,但如果发生这种情况,使用objc_sync_enter
可以允许Swift和Objective-C之间的协作。此答案还允许返回值。出于这些原因,我选择在我的项目中使用此答案。 - Rik Renich借鉴Bryan McLemore的回答,我扩展了它以支持Swift 2.0 defer能力中抛出异常时的安全处理。
func synchronized( lock:AnyObject, block:() throws -> Void ) rethrows
{
objc_sync_enter(lock)
defer {
objc_sync_exit(lock)
}
try block()
}
func synchronize<T>(lockObj: AnyObject!, closure: ()->T) -> T
{
objc_sync_enter(lockObj)
var retVal: T = closure()
objc_sync_exit(lockObj)
return retVal
}
func importantMethod(...) -> Bool {
return synchronize(self) {
if(feelLikeReturningTrue) { return true }
// do other things
if(feelLikeReturningTrueNow) { return true }
// more things
return whatIFeelLike ? true : false
}
}
class ImageCache {
private let queue = DispatchQueue(label: "sync queue")
private var storage: [String: UIImage] = [:]
public subscript(key: String) -> UIImage? {
get {
return queue.sync {
return storage[key]
}
}
set {
queue.sync {
storage[key] = newValue
}
}
}
}
你可以使用带有屏障的并发队列来加快读取速度。同步和异步读取同时进行,写入新值时会等待之前的操作完成。
class ImageCache {
private let queue = DispatchQueue(label: "with barriers", attributes: .concurrent)
private var storage: [String: UIImage] = [:]
func get(_ key: String) -> UIImage? {
return queue.sync { [weak self] in
guard let self = self else { return nil }
return self.storage[key]
}
}
func set(_ image: UIImage, for key: String) {
queue.async(flags: .barrier) { [weak self] in
guard let self = self else { return }
self.storage[key] = image
}
}
}
removeFirst()
创建包装器时,该怎么办? - ScottyBlades