SWIFT 3 GCD锁定变量和BLOCK_AND_RELEASE错误 [英] Swift 3 GCD lock variable and block_and_release error

查看:24
本文介绍了SWIFT 3 GCD锁定变量和BLOCK_AND_RELEASE错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用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不断检查inn queeMsgSent中的一些条件,并在主线程id Required中调用其他函数addMsgSentToQueueue和DeleteMsgSentWithID。我正在使用某种锁定机制,但它不能正常工作

推荐答案

我强烈建议您使用中央调度中心提供的DispatchQueue(s),它们使多线程管理更加轻松。

命令

让我们从您的命令类开始

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

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

队列

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

这是我们的班级不应该与DispatchQueue的概念混淆!

  1. Command推入队列
  2. 从队列中删除Command
  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属性是一个保存元组列表的数组,每个元组有两个组件:DateCommand

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

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

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

我们写入上述闭包👆👆👆的每个代码都被添加到我们的串行调度队列

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

最终考虑事项

以下代码块有问题

while true {
    ...
}

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

如您所见,我将其替换为

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

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

当然,更改秒数以更好地适应您的方案取决于您。

这篇关于SWIFT 3 GCD锁定变量和BLOCK_AND_RELEASE错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆