使用 os_unfair_lock_lock 进行快速访问竞赛

我制作了一个自定义属性包装器,它提供了一种使用 os_unfair_lock 在互斥上下文中访问数据的方法。在启用 TSAN 的情况下测试我的包装器后,在使用 os_unfair_lock_lock 获取锁时报告了访问竞争错误(如下图所示)

使用 os_unfair_lock_lock 进行快速访问竞赛

不知何故,TSAN 报告了一个据称是线程安全的锁定结构,但事实并非如此。这是怎么回事?

xiaoxi8892 回答:使用 os_unfair_lock_lock 进行快速访问竞赛

根据 WWDC 2016 演讲“在 Swift 3 中使用 GCD 进行并发编程”,演讲者在 18:07 左右表示

传统上您会使用锁。而在 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 */

UnfairLock.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
,

您的 self-answer 的另一种替代方法(可能更直接)是直接在 Swift 中堆分配锁,而不是桥接到 Objective-C 来执行此操作。 Objective-C 方法通过从具有不同语义的不同语言调用锁函数来避免这个问题——C 和 Objective-C 不会移动或通过 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 中是引用类型——虽然 Swift 可以移动指针值本身,但它引用的内存将保持不变(并且有效)。

如果你走这条路,当你想拆锁的时候不要忘记去初始化和释放内存:

lock.deinitialize(count: 1)
lock.deallocate()

如果您愿意,您可以在 Swift 中创建一个类似的 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
    }
}
本文链接:https://www.f2er.com/434.html

大家都在问