gpt4 book ai didi

ios - Swift 3 GCD 锁变量和 block_and_release 错误

转载 作者:可可西里 更新时间:2023-11-01 00:56:43 45 4
gpt4 key购买 nike

我正在使用 Swift 3 GCD 在我的代码中执行一些操作。但我经常遇到 _dispatch_call_block_and_release 错误。我想这个错误背后的原因是因为不同的线程修改同一个变量,但我不确定如何解决问题。这是我的代码和解释:

我有一个在不同线程中访问和修改的变量:

var queueMsgSent: Dictionary<Date,BTCommand>? = nil


func lock(obj: AnyObject, blk:() -> ()) {
objc_sync_enter(obj)
blk()
objc_sync_exit(obj)
}

func addMsgSentToQueue(msg: BTCommands) {

if queueMsgSent == nil {
queueMsgSent = Dictionary.init()
}
let currentDate = Date()
lock(obj: queueMsgSent as AnyObject) {
queueMsgSent?.updateValue(msg, forKey: currentDate)
}
}

func deleteMsgSentWithId(id: Int) {

if queueMsgSent == nil { return }

for (date, msg) in queueMsgSent! {


if msg.isAck() == false && msg.getId()! == id {
lock(obj: queueMsgSent as AnyObject) {
queueMsgSent?.removeValue(forKey: date)
}
}
}
}

func runSent() -> Void {


while(true) {
if queueMsgSent == nil { continue }

for (date, msg) in queueMsgSent! {

if msg.isSent() == false {
mainSearchView?.btCom?.write(str: msg.getCommand()!)
msg.setSent(val: true)
lastMsgSent = Date()
continue
}

if msg.isAck() == true {
lock(obj: queueMsgSent as AnyObject) {
queueMsgSent?.removeValue(forKey: date)
}
continue
}



}
}

}

我启动 runSent 方法为:

  DispatchQueue.global().async(execute: runSent)

我需要 runSent 不断检查 queueMsgSent 中的一些条件,并且在必要的主线程 id 中调用其他函数 addMsgSentToQueueue 和 deleteMsgSentWithId。我正在使用一些锁定机制,但它无法正常工作

最佳答案

我强烈建议您使用 Grand Central Dispatch 提供的 DispatchQueue(s),它们使多线程管理变得更加容易。

命令

让我们从你的命令类开始

class Command {
let id: String
var isAck = false
var isSent = false

init(id:String) {
self.id = id
}
}

队列

现在我们可以构建我们的Queue类,它将提供以下功能

This is our class should not be confused with the concept of DispatchQueue!

  1. 命令插入队列
  2. 从队列中删除一个命令
  3. 开始处理队列中的所有元素

现在是代码:

class Queue {
typealias Element = (date:Date, command:Command)
private var storage: [Element] = []
private let serialQueue = DispatchQueue(label: "serialQueue")

func push(command:Command) {
serialQueue.async {
let newElement = (Date(), command)
self.storage.append(newElement)
}
}

func delete(by id: String) {
serialQueue.async {
guard let index = self.storage.index(where: { $0.command.id == id }) else { return }
self.storage.remove(at: index)
}
}

func startProcessing() {
Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in
self.processElements()
}
}

private func processElements() {
serialQueue.async {
// send messages where isSent == false
let shouldBeSent = self.storage.filter { !$0.command.isSent }
for elm in shouldBeSent {
// TODO: add here code to send message
elm.command.isSent = true
}

// remove from storage message where isAck == true
self.storage = self.storage.filter { !$0.command.isAck }
}
}
}

它是如何工作的?

如您所见,storage 属性是一个包含元组列表的数组,每个元组有 2 个组件:DateCommand

由于 storage 数组由多个线程访问,我们需要确保以线程安全的方式访问它。

因此,每次我们访问storage 时,我们都会将我们的代码包装到这里

serialQueue.async {
// access self.storage safely
}

我们写入闭包 👆👆👆 的每个代码都会添加到我们的 Serial Dispatch Queue

串行队列当时确实处理了 1 个关闭。这就是我们的存储属性以线程安全方式访问的原因!

enter image description here

最终考虑

下面的代码块是邪恶的

while true {
...
}

它确实使用了所有可用的 CPU 时间,它确实卡住了 UI(在主线程上执行时)并释放了电池。

如你所见,我将其替换为

Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in
self.processElements()
}

每 10 秒调用一次 self.processElements(),为 CPU 留出充足的时间来处理其他线程。

当然,您可以更改秒数以更好地适应您的场景。

关于ios - Swift 3 GCD 锁变量和 block_and_release 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45640868/

45 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com