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

Swift Actor与Continuation结合的线程安全性问题咨询

问题分析与代码修正

你的代码在activeTask管理、Continuation安全使用以及Actor可重入性适配这几个点上存在问题,下面逐一说明并给出修正后的实现:

核心问题点

  • activeTask 生命周期管理错误:启动扫描后立刻将activeTask置为nil,会导致后续调用无法识别当前正在进行的扫描任务,进而重复创建新任务,违背了跟踪活跃任务的初衷。
  • Continuation 安全与线程适配:CBCentralManager的回调并非在Actor的串行队列执行,直接在回调中resume continuation可能引发线程安全问题;同时未处理蓝牙状态错误(如蓝牙未开启)的场景,会导致continuation永久挂起。
  • 类型不匹配:withCheckedThrowingContinuation的泛型类型是BLEDevice,但外层Task和返回值是BLEDevice?,存在类型不一致问题。

修正后的代码实现

public actor MyNewDelegateSub {
    private var activeTask: Task<BLEDevice, Error>?
    private let bleDelegate: BluetoothLEDelegate
    
    public init() {
        let delegate = BluetoothLEDelegate()
        self.bleDelegate = delegate
        // 将Actor的回调处理逻辑传给delegate,确保在Actor上下文处理resume
        delegate.onDeviceDiscovered = { [weak self] device in
            Task { [weak self] in
                await self?.handleDeviceDiscovery(device: device)
            }
        }
        delegate.onScanError = { [weak self] error in
            Task { [weak self] in
                await self?.handleScanError(error: error)
            }
        }
    }
    
    public func scanForDevice() async throws -> BLEDevice {
        // 若已有活跃任务,直接等待其结果
        if let existingTask = activeTask {
            return try await existingTask.value
        }
        
        let task = Task { [weak self] in
            guard let self = self else { throw CancellationError() }
            return try await withCheckedThrowingContinuation { continuation in
                // 将continuation暂存到Actor内部,而非直接给delegate
                self.bleDelegate.setContinuation(continuation)
                do {
                    try self.bleDelegate.startScan()
                } catch {
                    continuation.resume(throwing: error)
                }
            }
        }
        
        activeTask = task
        
        do {
            let device = try await task.value
            activeTask = nil // 任务完成后清除活跃任务标记
            return device
        } catch {
            activeTask = nil // 任务失败或取消后也需清除标记
            throw error
        }
    }
    
    private func handleDeviceDiscovery(device: BLEDevice) {
        bleDelegate.resumeContinuation(with: .success(device))
        activeTask = nil
    }
    
    private func handleScanError(error: Error) {
        bleDelegate.resumeContinuation(with: .failure(error))
        activeTask = nil
    }
}

private final class BluetoothLEDelegate: NSObject, CBCentralManagerDelegate {
    private let centralManager = CBCentralManager()
    private var continuation: CheckedContinuation<BLEDevice, Error>?
    var onDeviceDiscovered: ((BLEDevice) -> Void)?
    var onScanError: ((Error) -> Void)?
    
    override init() {
        super.init()
        centralManager.delegate = self
    }
    
    func setContinuation(_ continuation: CheckedContinuation<BLEDevice, Error>?) {
        self.continuation = continuation
    }
    
    func startScan() throws {
        guard centralManager.state == .poweredOn else {
            throw BluetoothError.unavailable(centralManager.state)
        }
        centralManager.scanForPeripherals(withServices: nil)
    }
    
    func resumeContinuation(with result: Result<BLEDevice, Error>) {
        continuation?.resume(with: result)
        continuation = nil
        centralManager.stopScan()
    }
    
    // MARK: - CBCentralManagerDelegate
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        // 蓝牙状态变更为不可用时,触发错误回调
        if central.state != .poweredOn, continuation != nil {
            onScanError?(BluetoothError.unavailable(central.state))
        }
    }
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        onDeviceDiscovered?(BLEDevice(peripheral))
    }
}

// 自定义蓝牙错误类型
enum BluetoothError: Error {
    case unavailable(CBCentralManager.State)
}

// 假设的BLEDevice模型
struct BLEDevice {
    let peripheral: CBPeripheral
    init(_ peripheral: CBPeripheral) {
        self.peripheral = peripheral
    }
}

关键修正说明

  • activeTask 正确管理:仅在任务成功、失败或取消后才清除activeTask,确保并发调用能正确识别并等待已有任务结果,适配Actor的可重入特性。
  • Continuation 线程安全:通过闭包将蓝牙回调转发到Actor上下文处理,避免在非Actor队列直接操作continuation;同时统一在Actor内管理continuation的resume逻辑,确保线程安全。
  • 错误场景覆盖:新增蓝牙状态错误处理,避免因蓝牙未开启导致continuation永久挂起;任务取消时也会自动清理continuation和activeTask。
  • 类型一致性:统一返回值类型为非可选的BLEDevice,若扫描无结果可通过自定义错误抛出,语义更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 21:53:22