我已经搜索了Swift的书籍,但找不到@synchronized的Swift版本。我如何做互斥在Swift?


当前回答

细节

Xcode 8.3.1, Swift 3.1

Task

从不同的线程读写值(异步)。

Code

class AsyncObject<T>:CustomStringConvertible {
    private var _value: T
    public private(set) var dispatchQueueName: String
   
    let dispatchQueue: DispatchQueue
    
    init (value: T, dispatchQueueName: String) {
        _value = value
        self.dispatchQueueName = dispatchQueueName
        dispatchQueue = DispatchQueue(label: dispatchQueueName)
    }
    
    func setValue(with closure: @escaping (_ currentValue: T)->(T) ) {
        dispatchQueue.sync { [weak self] in
            if let _self = self {
                _self._value = closure(_self._value)
            }
        }
    }
    
    func getValue(with closure: @escaping (_ currentValue: T)->() ) {
        dispatchQueue.sync { [weak self] in
            if let _self = self {
                closure(_self._value)
            }
        }
    }
    
    
    var value: T {
        get {
            return dispatchQueue.sync { _value }
        }
        
        set (newValue) {
            dispatchQueue.sync { _value = newValue }
        }
    }

    var description: String {
        return "\(_value)"
    }
}

使用

print("Single read/write action")
// Use it when when you need to make single action
let obj = AsyncObject<Int>(value: 0, dispatchQueueName: "Dispatch0")
obj.value = 100
let x = obj.value
print(x)

print("Write action in block")
// Use it when when you need to make many action
obj.setValue{ (current) -> (Int) in
    let newValue = current*2
    print("previous: \(current), new: \(newValue)")
    return newValue
}

完整的样品

扩展DispatchGroup

extension DispatchGroup {
    
    class func loop(repeatNumber: Int, action: @escaping (_ index: Int)->(), completion: @escaping ()->()) {
        let group = DispatchGroup()
        for index in 0...repeatNumber {
            group.enter()
            DispatchQueue.global(qos: .utility).async {
                action(index)
                group.leave()
            }
        }
        
        group.notify(queue: DispatchQueue.global(qos: .userInitiated)) {
            completion()
        }
    }
}

类ViewController

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        //sample1()
        sample2()
    }
    
    func sample1() {
        print("=================================================\nsample with variable")
        
        let obj = AsyncObject<Int>(value: 0, dispatchQueueName: "Dispatch1")
        
        DispatchGroup.loop(repeatNumber: 5, action: { index in
            obj.value = index
        }) {
            print("\(obj.value)")
        }
    }
    
    func sample2() {
        print("\n=================================================\nsample with array")
        let arr = AsyncObject<[Int]>(value: [], dispatchQueueName: "Dispatch2")
        DispatchGroup.loop(repeatNumber: 15, action: { index in
            arr.setValue{ (current) -> ([Int]) in
                var array = current
                array.append(index*index)
                print("index: \(index), value \(array[array.count-1])")
                return array
            }
        }) {
            print("\(arr.value)")
        }
    }
}

其他回答

我自己也在寻找这个,并得出结论,在swift中还没有对此的原生构造。

我确实根据我从Matt Bridges和其他人那里看到的一些代码编写了这个小的帮助函数。

func synced(_ lock: Any, closure: () -> ()) {
    objc_sync_enter(lock)
    closure()
    objc_sync_exit(lock)
}

用法非常简单

synced(self) {
    println("This is a synchronized closure")
}

我发现了一个问题。在这一点上,传入一个数组作为lock参数似乎会导致一个非常迟钝的编译器错误。除此之外,虽然它似乎工作如所愿。

Bitcast requires both operands to be pointer or neither
  %26 = bitcast i64 %25 to %objc_object*, !dbg !378
LLVM ERROR: Broken function found, compilation aborted!

为什么要让锁变得困难和麻烦呢? 使用分派障碍。

调度障碍在并发队列中创建同步点。 当它正在运行时,队列上的其他块都不允许运行,即使它是并发的并且其他内核可用。 如果这听起来像一个排他(写)锁,它是。 无障碍块可以被认为是共享(读)锁。 只要所有对资源的访问都是通过队列执行的,barrier就能提供非常廉价的同步。

基于ɲeuroburɳ,测试一个子类用例

class Foo: NSObject {
    func test() {
        print("1")
        objc_sync_enter(self)
        defer {
            objc_sync_exit(self)
            print("3")
        }

        print("2")
    }
}


class Foo2: Foo {
    override func test() {
        super.test()

        print("11")
        objc_sync_enter(self)
        defer {
            print("33")
            objc_sync_exit(self)
        }

        print("22")
    }
}

let test = Foo2()
test.test()

输出:

1
2
3
11
22
33

在Swift4中使用NSLock:

let lock = NSLock()
lock.lock()
if isRunning == true {
        print("Service IS running ==> please wait")
        return
} else {
    print("Service not running")
}
isRunning = true
lock.unlock()

警告 NSLock类使用POSIX线程来实现它的锁定行为。当向NSLock对象发送解锁消息时,必须确保该消息是从发送初始锁定消息的同一个线程发送的。从不同的线程解锁锁可能会导致未定义的行为。

类似于Objective-C中的@synchronized指令,在Swift中可以有任意的返回类型和漂亮的重抛出行为。

// Swift 3
func synchronized<T>(_ lock: AnyObject, _ body: () throws -> T) rethrows -> T {
    objc_sync_enter(lock)
    defer { objc_sync_exit(lock) }
    return try body()
}

使用defer语句可以直接返回一个值,而不需要引入临时变量。


在Swift 2中,为闭包添加@noescape属性可以进行更多的优化:

// Swift 2
func synchronized<T>(lock: AnyObject, @noescape _ body: () throws -> T) rethrows -> T {
    objc_sync_enter(lock)
    defer { objc_sync_exit(lock) }
    return try body()
}

基于GNewc[1](我喜欢任意返回类型)和Tod Cunningham[2](我喜欢延迟)的答案。