You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现异步update函数的线程安全?并发调用引发数据异常

异步函数的线程安全问题及解决办法

问题场景

现有以下异步函数代码片段:

public func update(tables: [LocalTableItem]) async throws -> UpdateResult {
        
        let result = try await store.remove(tables: tables)
        print("** removing table \(tables.last!.name)")
        
        switch result {
        case .success:
            let insertResult = try await self.store.insert(tables: tables)
            print("** inserting table \(tables.last!.name)")

当通过多任务并发调用该函数时:

Task {
    _ = try await store.update(tables: [updatedTable])
}

日志输出显示:

** removing table icradle_loler_engineer.

** removing table icradle_loler_engineer.

** inserting table icradle_loler_engineer.

** inserting table icradle_loler_engineer.

此时remove与insert的调用顺序被打乱,导致数据库出现重复条目。尝试将类改为Actor后问题仍未解决,需实现该异步函数的线程安全。

可行解决方案

1. 修正Actor的使用方式

改成Actor后无效,通常是因为store对象未被正确隔离,或者update方法的调用路径没有经过Actor串行化。正确的实现方式:

  • 将包含update方法的类定义为Actor,同时确保内部依赖的store本身是线程安全的(比如store也是Actor,或其方法被Actor管控)。
  • Actor会自动保证异步调用的串行执行,确保每个update的“删除-插入”流程完整执行后,再处理下一个任务。示例代码:
actor StoreManager {
    private let store: YourStoreImplementation

    init(store: YourStoreImplementation) {
        self.store = store
    }

    func update(tables: [LocalTableItem]) async throws -> UpdateResult {
        let result = try await store.remove(tables: tables)
        print("** removing table \(tables.last!.name)")
        
        switch result {
        case .success:
            let insertResult = try await self.store.insert(tables: tables)
            print("** inserting table \(tables.last!.name)")
            // 补充返回逻辑,比如返回insertResult
        case .failure(let error):
            throw error
        }
    }
}

调用时必须通过Actor实例发起,不能直接绕开Actor访问内部方法:

let manager = StoreManager(store: yourStoreInstance)
Task {
    _ = try await manager.update(tables: [updatedTable])
}

2. 用锁同步原子操作

如果不想使用Actor,可以用NSLock或os_unfair_lock将“删除-插入”操作包裹为原子操作,确保同一时间只有一个线程执行该流程:

private let updateLock = NSLock()

public func update(tables: [LocalTableItem]) async throws -> UpdateResult {
    updateLock.lock()
    defer { updateLock.unlock() } // 确保锁最终被释放
    
    let result = try await store.remove(tables: tables)
    print("** removing table \(tables.last!.name)")
    
    switch result {
    case .success:
        let insertResult = try await self.store.insert(tables: tables)
        print("** inserting table \(tables.last!.name)")
        // 补充返回逻辑
    case .failure(let error):
        throw error
    }
}

注意:如果store的remove/insert方法本身耗时较长,这种方式会阻塞其他任务,影响并发性能。

3. 数据库事务保证原子性

最可靠的方式是在数据库层面开启事务,将“删除-插入”操作纳入同一个事务中。数据库会保证事务的原子性,要么全部执行成功,要么全部回滚,避免出现中间状态:

public func update(tables: [LocalTableItem]) async throws -> UpdateResult {
    try await store.beginTransaction()
    defer {
        // 若事务未提交,自动回滚
        if store.isInTransaction {
            try? store.rollbackTransaction()
        }
    }
    
    let result = try await store.remove(tables: tables)
    print("** removing table \(tables.last!.name)")
    
    switch result {
    case .success:
        let insertResult = try await self.store.insert(tables: tables)
        print("** inserting table \(tables.last!.name)")
        try await store.commitTransaction()
        return insertResult // 或自定义的成功结果
    case .failure(let error):
        throw error
    }
}

不同数据库的事务API有所差异,需根据你使用的数据库框架调整具体实现。

内容的提问来源于stack exchange,提问作者user3010251

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 11:03:16