如何在Swift 3中创建线程安全变量?

时间:2017-03-15 11:58:25

标签: ios swift multithreading thread-safety atomic

我有单身类

class DeviceController:NSObject, CocoaMQTTDelegate {
     static let sharedInstance = DeviceController()
     var deviceOnArray:[String] = []
     var deviceOffArray:[String] = []
     private override init() {

        clientID = "xyz-" + String(ProcessInfo().processIdentifier)
        mqtt = CocoaMQTT(clientID: clientID, host: "device_controller.xyz.net", port: 1883)
        mqtt.username = "username"
        mqtt.password = "password"
        mqtt.willMessage = CocoaMQTTWill(topic: "/will", message: "dieout")
        mqtt.keepAlive = 30
        mqtt.cleanSession = true
        DeviceController.isConnecting = true
        super.init()
        mqtt.delegate = self
        mqtt.connect()
        self.registerBackgroundTask()
    }
   func sendArm(topic:String){
      // add device to deviceOnArray
   }
   func sendDisarm(topic:String){
      // remove device from deviceOnArray if exist here if I check by code that device is in array it returns false but on console if print it contains the device, It only heppens when I call sendArm and sendDisarm with a second.
     let lockQueue = DispatchQueue(label: "com.test.LockQueue")
        lockQueue.sync() {
           // all code now inside this
         }
     // I also used above code but it's not working
   }


}

如果您阅读了代码,那么您将知道我在从deviceOnArray / deviceOffArray读取正确值时遇到问题,我不知道如何解释这个问题,但我认为我需要的是Obj-C atomic线程安全变量。知道怎么创造一个吗?

3 个答案:

答案 0 :(得分:3)

您可以使用串行调度队列来确保仅以线程安全的方式更新阵列。

最好将deviceOnArray属性更改为private,以确保其他对象无法访问它。如果需要将此数组公开给其他对象,请通过计算属性执行此操作。 e.g。

class DeviceController:NSObject, CocoaMQTTDelegate {
     static let sharedInstance = DeviceController()
     private var deviceOnArray:[String] = []
     var deviceOn: [String] {
         return self.deviceOnArray
     }

     var deviceOffArray:[String] = []
     private let dispatchQueue = DispatchQueue(label:"DeviceControllerQueue")

     private override init() {

        clientID = "xyz-" + String(ProcessInfo().processIdentifier)
        mqtt = CocoaMQTT(clientID: clientID, host: "device_controller.xyz.net", port: 1883)
        mqtt.username = "username"
        mqtt.password = "password"
        mqtt.willMessage = CocoaMQTTWill(topic: "/will", message: "dieout")
        mqtt.keepAlive = 30
        mqtt.cleanSession = true
        DeviceController.isConnecting = true
        super.init()
        mqtt.delegate = self
        mqtt.connect()
        self.registerBackgroundTask()
    }
   func sendArm(topic:String){
      // add device to deviceOnArray
       self.dispatchQueue.sync {
           deviceOnArray.append(topic)
       }
   }

   func sendDisarm(topic:String){
      // remove device from deviceOnArray if exist here.
       self.dispatchQueue.sync {
           if let index = self.deviceOnArray.index(of: topic) {
               self.deviceOnArray.remove(at: index)
           }
       }
   }
}

答案 1 :(得分:2)

详细信息

  • Xcode 10.2.1(10E1001),Swift 5

解决方案

import Foundation

class AtomicValue<T> {

    private let semaphore = DispatchSemaphore(value: 1)
    private var _value: T

    init (value: T) { _value = value }
    //deinit { print("____ \(self) deinited") }

    var value: T {
        get {
            wait(); defer { signal() }
            let result = _value
            return result
        }

        set (value) {
            wait(); defer { signal() }
            _value = value
        }
    }

    func set(closure: ((_ currentValue: T) -> T)?) {
        wait(); defer { signal() }
        guard let value = closure?(_value) else { return }
        _value = value
    }

    func get(closure: ((_ currentValue: T) -> Void)?) {
        wait(); defer { signal() }
        closure?(_value)
    }

    private func wait() { semaphore.wait() }
    private func signal() { semaphore.signal() }
}

用法

let atomicValue = AtomicValue(value: 0)

// Usage
print("!\(atomicValue.value)")
atomicValue.value += 1
atomicValue.set { currentValue -> Int in
    // Several actions
    return currentValue + 1
}
atomicValue.get { currentValue in
    // Several actions
    print("!\(currentValue)")
}

样本

let atomicValue = AtomicValue(value: 0)

DispatchQueue.global(qos: .utility).async {
    (0..<10).forEach { _ in
        atomicValue.set { currentValue -> Int in
            let newValue = currentValue + 1
            print("utility queue: \(newValue)")
            return newValue
        }
        usleep(100_000)
    }
}

DispatchQueue.global(qos: .default).async {
    (0..<10).forEach { _ in
        atomicValue.set { currentValue -> Int in
            let newValue = currentValue + 1
            print("default queue: \(newValue)")
            return newValue
        }
        usleep(100_000)
    }
}

结果(日志)

default queue: 1
utility queue: 2
default queue: 3
utility queue: 4
default queue: 5
utility queue: 6
default queue: 7
utility queue: 8
default queue: 9
utility queue: 10
default queue: 11
utility queue: 12
default queue: 13
utility queue: 14
default queue: 15
utility queue: 16
default queue: 17
utility queue: 18
default queue: 19
utility queue: 20

答案 2 :(得分:1)

原子属性对您没有帮助。它们旨在同步分配属性作为整体而不是内部(例如,它们不同步向元素插入/移除元素)。它们几乎只能确保正确的保留/释放/自动释放调用,以防止程序崩溃/泄漏。

您需要的是Dispatch​Semaphore或类似的内容(或者更多原生内容,posix pthread_mutex内容)以确保互斥访问。