CoreBluetooth iOS 如果使用 WriteWithoutResponse 时可能发生数据竞争
CoreBluetooth iOS if data race possible when using WriteWithoutResponse
我想用我的外围设备发送一个字节流class。所以我在问自己是否需要线程安全队列,或者删除它以解锁流程是否安全。
我有以下结构
private struct WriteWithoutResponseContext {
let data: Data
let characteristic: Characteristic
}
还有这两个属性
private var writeWithoutResponseContextQueue = Queue<WriteWithoutResponseContext>()
private let writeWithoutResponseContextCachingDispatchQueue = DispatchQueue(label: "thread-safe-unsent-data-caching", attributes: .concurrent)
我正在使用带有屏障的队列来确保所有内容都被阻塞,直到我在 canSendWriteWithoutResponse 为 false 时将其添加到队列中。
我们怎么知道在检查 canSendWriteWithoutResponse 和上下文排队之间没有发生 peripheralIsReady?
func writeValueWithoutResponse(_ data: Data, for characteristic: Characteristic) {
if self.cbPeripheral.canSendWriteWithoutResponse {
self.cbPeripheral.writeValue(data, for: characteristic, type: .withoutResponse)
} else {
self.writeWithoutResponseContextCachingDispatchQueue.async(flags: .barrier) {
let context = WriteWithoutResponseContext(data: data, characteristic: characteristic)
self.writeWithoutResponseContextQueue.enqueue(context)
}
}
}
func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
self.writeWithoutResponseContextCachingDispatchQueue.sync {
if let unsentContext = self.writeWithoutResponseContextQueue.dequeue() {
self.cbPeripheral.writeValue(unsentContext.data, for: unsentContext.characteristic, type: .withoutResponse)
}
}
}
为了安全起见,您需要指定您的 CentralManger
正在运行的队列,例如,您的中央管理器在其中调度中央角色事件。
如果您没有指定任何内容,CentralManager 将使用主队列。
self.centralManager = CBCentralManager(delegate: self, queue: nil, options: options)
所以在我的例子中,只需创建带有串行队列的 CentralManager 就可以了。
let serialQueue = DispatchQueue(label: "com.Whosebug.64819549", qos: .userInitiated)
self.centralManager = CBCentralManager(delegate: self, queue: serialQueue, options: options)
有了它我就不需要屏障了
我想用我的外围设备发送一个字节流class。所以我在问自己是否需要线程安全队列,或者删除它以解锁流程是否安全。
我有以下结构
private struct WriteWithoutResponseContext {
let data: Data
let characteristic: Characteristic
}
还有这两个属性
private var writeWithoutResponseContextQueue = Queue<WriteWithoutResponseContext>()
private let writeWithoutResponseContextCachingDispatchQueue = DispatchQueue(label: "thread-safe-unsent-data-caching", attributes: .concurrent)
我正在使用带有屏障的队列来确保所有内容都被阻塞,直到我在 canSendWriteWithoutResponse 为 false 时将其添加到队列中。
我们怎么知道在检查 canSendWriteWithoutResponse 和上下文排队之间没有发生 peripheralIsReady?
func writeValueWithoutResponse(_ data: Data, for characteristic: Characteristic) {
if self.cbPeripheral.canSendWriteWithoutResponse {
self.cbPeripheral.writeValue(data, for: characteristic, type: .withoutResponse)
} else {
self.writeWithoutResponseContextCachingDispatchQueue.async(flags: .barrier) {
let context = WriteWithoutResponseContext(data: data, characteristic: characteristic)
self.writeWithoutResponseContextQueue.enqueue(context)
}
}
}
func peripheralIsReady(toSendWriteWithoutResponse peripheral: CBPeripheral) {
self.writeWithoutResponseContextCachingDispatchQueue.sync {
if let unsentContext = self.writeWithoutResponseContextQueue.dequeue() {
self.cbPeripheral.writeValue(unsentContext.data, for: unsentContext.characteristic, type: .withoutResponse)
}
}
}
为了安全起见,您需要指定您的 CentralManger
正在运行的队列,例如,您的中央管理器在其中调度中央角色事件。
如果您没有指定任何内容,CentralManager 将使用主队列。
self.centralManager = CBCentralManager(delegate: self, queue: nil, options: options)
所以在我的例子中,只需创建带有串行队列的 CentralManager 就可以了。
let serialQueue = DispatchQueue(label: "com.Whosebug.64819549", qos: .userInitiated)
self.centralManager = CBCentralManager(delegate: self, queue: serialQueue, options: options)
有了它我就不需要屏障了