首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在异步方法中实现线程安全?

如何在异步方法中实现线程安全?
EN

Stack Overflow用户
提问于 2022-10-26 15:07:40
回答 2查看 113关注 0票数 1

我正在尝试阅读密钥链,但文档警告我如下:

SecItemCopyMatching阻塞调用线程,因此如果从主线程调用,它会导致应用程序的UI挂起。相反,从后台调度队列或异步函数调用SecItemCopyMatching。

来源

因此,我想编写一个在后台运行的异步方法。

代码语言:javascript
复制
actor Keychain {
    public static let standard = Keychain()
    
    public enum Error: Swift.Error {
        case failed(String)
    }
    
    public func get(_ key: String) async throws -> Data? {
        let backgroundTask = Task(priority: .background) {
            var query: [String: Any] = [
                type(of: self).klass       : kSecClassGenericPassword,
                type(of: self).attrAccount : key,
                type(of: self).matchLimit  : kSecMatchLimitOne,
                type(of: self).returnData  : kCFBooleanTrue as CFBoolean
            ]
            
            var item: CFTypeRef?
            let status = SecItemCopyMatching(query as CFDictionary, &item)
            
            guard status == errSecSuccess else {
                if let errorMessage = SecCopyErrorMessageString(status, nil) {
                    throw Error.failed(String(errorMessage))
                } else {
                    throw Error.failed("unsupported")
                }
            }
            
            return item as? Data
        }
        return try await backgroundTask.value
    }
}

我的问题是。演员会不会已经让线程安全了?

通常,为了安全起见,我会添加一个NSLock

代码语言:javascript
复制
public func get(_ key: String) async throws -> Data? {
    lock.lock()
    defer { lock.unlock() }
    
    (...)
    
    return try await task.value
}

然而,现在我收到了一个警告Instance method 'lock' is unavailable from asynchronous contexts; Use async-safe scoped locking instead; this is an error in Swift 6

那么我是如何做到这一点的呢?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-10-26 15:30:35

是的,演员们会让它安全的。不需要锁之类的。参见WWDC 2021视频用斯威夫特演员保护易变状态

顺便说一句,既然你已经在演员身上看到了这一点,你真的不需要让get(_:)成为一个async方法。演员已经在后台线程上运行。因此,删除async限定符,然后删除Task

代码语言:javascript
复制
actor Keychain {
    ...
    
    public func get(_ key: String) throws -> Data? {
        var query: [String: Any] = [
            type(of: self).klass       : kSecClassGenericPassword,
            type(of: self).attrAccount : key,
            type(of: self).matchLimit  : kSecMatchLimitOne,
            type(of: self).returnData  : kCFBooleanTrue as CFBoolean
        ]
        
        var item: CFTypeRef?
        let status = SecItemCopyMatching(query as CFDictionary, &item)
        
        guard status == errSecSuccess else {
            if let errorMessage = SecCopyErrorMessageString(status, nil) {
                throw Error.failed(String(errorMessage))
            } else {
                throw Error.failed("unsupported")
            }
        }
        
        return item as? Data
    }
}
票数 2
EN

Stack Overflow用户

发布于 2022-10-26 15:36:29

为什么要将密钥链操作放在不需要的异步块中。你不用它就走吧。密钥链操作是线程安全的,这是由苹果公司保证的。请看这份文件。苹果

因此,在不使用异步块的情况下这样做,并将所有ui更改放在主线程中(如果还没有)。UI更改需要在主线程中进行。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74210041

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档