我制作了一个自定义属性包装器,它提供了一种使用
os_unfair_lock
在互斥上下文中访问数据的方法。在启用 TSAN 的情况下测试我的包装器后,在使用 os_unfair_lock_lock
获取锁时报告了访问争用错误(如下图所示)
不知何故,TSAN 报告了一个被认为是线程安全的锁定结构,但事实并非如此。这是怎么回事?
实现“自我回答”的另一种(可能更直接)方法是直接在 Swift 中堆分配锁,而不是桥接到 Objective-C 来执行此操作。 Objective-C 方法通过使用不同的语义从不同的语言调用锁定函数来避免这个问题 - C 和 Objective-C 不会 move 或通过 inout 引用传递给函数的逻辑删除值类型;但你也可以通过完全不使用 inout 引用来避免纯 Swift 中的问题:
let lock = UnsafeMutablePointer<os_unfair_lock>.allocate(capacity: 1)
lock.initialize(to: .init())
// later:
os_unfair_lock_lock(lock)
defer { os_unfair_lock_unlock(lock) }
堆分配允许您直接将指针传递到函数中,而指针在 Swift 中是reference
类型 - 虽然 Swift 可以移动指针值本身,但它引用的内存将保持不变(并且有效)。 如果你走这条路,当你想拆掉锁时,不要忘记去初始化和释放内存:
lock.deinitialize(count: 1)
lock.deallocate()
UnfairLock
界面,包括您自己的
mutexExecute
之类的功能:typealias UnfairLock = UnsafeMutablePointer<os_unfair_lock>
extension UnfairLock {
static func createLock() -> UnfairLock {
let l = UnfairLock.allocate(capacity: 1)
l.initialize(to: .init())
return l
}
static func destructLock(_ lock: UnfairLock) {
lock.deinitialize(count: 1)
lock.deallocate()
}
func whileLocked<T>(_ action: () throws -> T) rethrows -> T {
os_unfair_lock_lock(self)
defer { os_unfair_lock_unlock(self) }
return try action()
}
}
用途:
init() {
lock = UnfairLock.createLock()
}
deinit {
UnfairLock.destructLock(lock)
}
func performThing() -> Foo {
return lock.whileLocked {
// some operation that returns a Foo
}
}
传统上您会使用锁。在 Swift 中,由于您可以使用整个 Darwin 模块,因此您实际上会看到基于结构的传统 C 锁。
然而[强调],Swift 假设任何结构体都可以移动,而这不适用于互斥锁或锁。
解决方案是桥接到 Objective-C 并创建一个类,将
os_unfair_lock
包装为 ivar:
如果你想要更小的东西并且看起来像你在 C 中拥有的锁,那么你必须调用 Objective-C 并在 Objective-C 中引入一个基类,它将你的锁作为 ivar在这种情况下,类似
UnfairLock.h
#ifndef UnfairLock_h
#define UnfairLock_h
@import Foundation;
@import os;
@interface UnfairLock : NSObject
-(void)unfairlyAcquire;
-(void)unlock;
@end
#endif /* UnfairLock_h */
不公平锁.m
#import <Foundation/Foundation.h>
#import "UnfairLock.h"
@implementation UnfairLock {
os_unfair_lock _lock;
}
-(instancetype)init {
self = [super init];
if (self) {
_lock = OS_UNFAIR_LOCK_INIT;
}
return self;
}
-(void)unfairlyAcquire {
os_unfair_lock_lock(&_lock);
}
-(void)unlock {
os_unfair_lock_unlock(&_lock);
}
@end
import os.lock
class Locker {
func lock() {
os_unfair_lock_lock(oslock)
}
func unlock() {
os_unfair_lock_unlock(oslock)
}
func trylock() -> Bool {
return os_unfair_lock_trylock(oslock)
}
let oslock = {
let lock1 = UnsafeMutablePointer<os_unfair_lock>.allocate(capacity: 1)
lock1.initialize(to: .init())
return lock1
}()
deinit {
oslock.deinitialize(count: 1)
oslock.deallocate()
}
}
// property
let lock = Locker()
func example() {
lock.lock()
/* critical code */
lock.unlock()
}
甚至可以将Locker放入数组和字典中。
以您的摘录为例:
let myLock = OSAllocatedUnfairLock()
func mutexExecute(_ block: (inout Value) -> Void) {
myLock.withLock {
block(&_wrappedValue.storage)
}
}
请注意,通过使用低级锁定,您的代码很容易出现死锁。 基于结构化并发(异步/等待、参与者、延续)的实现可以防止死锁,如果不需要低级性能,则应考虑。