Swift 3 GCD lock variable and block_and_release error

人盡茶涼 提交于 2019-12-04 17:51:48

I strongly suggest you to use the DispatchQueue(s) provided by Grand Central Dispatch, they makes multithreading management much easier.

Command

Let's start with your command class

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

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

Queue

Now we can build our Queue class, it will provide the following functionalities

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

  1. push a Command into the queue
  2. delete a Command from the queue
  3. start the processing of all the elements into the queue

And now the code:

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 }
        }
    }
}

How does it work?

As you can see the storage property is an array holding a list of tuples, each tuple has 2 components: Date and Command.

Since the storage array is accesses by multiple threads we need to make sure it is accessed in a thread safe way.

So each time we access storage we wrap our code into this

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

Each code we write into the closure 👆👆👆 shown above is added to our Serial Dispatch Queue.

The Serial Queue does process 1 closure at the time. That's why our storage property is accessed in a thread safe way!

Final consideration

The following block of code is evil

while true {
    ...
}

It does use all the available CPU time, it does freeze the UI (when executed on the main thread) and discharge the battery.

As you can see I replaced it with

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

which calls self.processElements() every 10 seconds leaving plenty of time to the CPU to process other threads.

Of course it's up to you changing the number of seconds to better fit your scenario.

If you're uncomfortable with the objc mechanisms, you might take a look here. Using that, you create a PThreadMutex for the specific synchronizations you want to coordinate, then use mutex.fastsync{ *your code* } to segregate accesses. It's a simple, very lightweight mechanism using OS-level calls, but you'll have to watch out for creating deadlocks.

The example you provide depends on the object always being the same physical entity, because the objc lock uses the address as the ID of what's being synchronized. Because you seem to have to check everywhere for the existence of queueMsgSent, I'm wondering what the update value routine is doing - if it ever deletes the dictionary, expecting it to be created later, you'll have a potential race as different threads can be looking at different synchronizers.

Separately, your loop in runSent is a spin loop - if there's nothing to do, it's just going to burn CPU rather than waiting for work. Perhaps you could consider revising this to use semaphores or some more appropriate mechanism that would allow the workers to block when there's nothing to do?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!