什么是Swift等同于Objective-C的“@synchronized”?

时间:2014-06-04 19:26:00

标签: concurrency mutex swift

我搜索过Swift书,但找不到Swift版本的@synchronized。如何在Swift中进行互斥?

21 个答案:

答案 0 :(得分:169)

您可以使用GCD。它比@synchronized更冗长,但作为替代品:

let serialQueue = DispatchQueue(label: "com.test.mySerialQueue")
serialQueue.sync {
    // code
}

答案 1 :(得分:168)

我自己一直在寻找这个,并得出结论,这里没有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!

答案 2 :(得分:140)

我喜欢并使用这里的许多答案,所以我选择最适合你的方式。也就是说,当我需要像objective-c @synchronized这样的东西时,我更喜欢使用swift 2中引入的defer语句。

{ 
    objc_sync_enter(lock)
    defer { objc_sync_exit(lock) }

    //
    // code of critical section goes here
    //

} // <-- lock released when this block is exited

关于此方法的好处是,您的关键部分可以以任何所需的方式退出包含块(例如,returnbreakcontinuethrow )和&#34;无论程序控制如何转移,都会执行延迟声明中的语句。&#34; 1

答案 3 :(得分:75)

您可以在objc_sync_enter(obj: AnyObject?)objc_sync_exit(obj: AnyObject?)之间添加语句。 @synchronized关键字正在使用这些方法。即。

objc_sync_enter(self)
... synchronized code ...
objc_sync_exit(self)

答案 4 :(得分:71)

来自Objective-C的@synchronized指令的模拟可以在Swift中具有任意返回类型和良好的rethrows行为。

// 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](我喜欢defer)的答案。

答案 5 :(得分:34)

SWIFT 4

在Swift 4中,您可以使用GCD调度队列来锁定资源。

class MyObject {
    private var internalState: Int = 0
    private let internalQueue: DispatchQueue = DispatchQueue(label:"LockingQueue") // Serial by default

    var state: Int {
        get {
            return internalQueue.sync { internalState }
        }

        set (newState) {
            internalQueue.sync { internalState = newState }
        }
    }
} 

答案 6 :(得分:23)

要添加返回功能,您可以执行以下操作:

func synchronize<T>(lockObj: AnyObject!, closure: ()->T) -> T
{
  objc_sync_enter(lockObj)
  var retVal: T = closure()
  objc_sync_exit(lockObj)
  return retVal
}

随后,您可以使用以下方式调用它:

func importantMethod(...) -> Bool {
  return synchronize(self) {
    if(feelLikeReturningTrue) { return true }
    // do other things
    if(feelLikeReturningTrueNow) { return true }
    // more things
    return whatIFeelLike ? true : false
  }
}

答案 7 :(得分:23)

使用Bryan McLemore的回答,我将其扩展为支持使用Swift 2.0推迟能力投入安全庄园的对象。

func synchronized( lock:AnyObject, block:() throws -> Void ) rethrows
{
    objc_sync_enter(lock)
    defer {
        objc_sync_exit(lock)
    }

    try block()
}

答案 8 :(得分:13)

在现代Swift 5中,具有返回功能:

/**
Makes sure no other thread reenters the closure before the one running has not returned
*/
@discardableResult
public func synchronized<T>(_ lock: AnyObject, closure:() -> T) -> T {
    objc_sync_enter(lock)
    defer { objc_sync_exit(lock) }

    return closure()
}

像这样使用它,以利用返回值功能:

let returnedValue = synchronized(self) { 
     // Your code here
     return yourCode()
}

否则就这样:

synchronized(self) { 
     // Your code here
    yourCode()
}

答案 9 :(得分:10)

Swift 3

此代码具有重新输入功能,可以使用异步函数调用。在此代码中,在调用someAsyncFunc()之后,串行队列上的另一个函数闭包将被处理但被semaphore.wait()阻塞,直到调用signal()。不应该使用internalQueue.sync,因为如果我没弄错的话会阻塞主线程。

let internalQueue = DispatchQueue(label: "serialQueue")
let semaphore = DispatchSemaphore(value: 1)

internalQueue.async {

    self.semaphore.wait()

    // Critical section

    someAsyncFunc() {

        // Do some work here

        self.semaphore.signal()
    }
}
如果没有错误处理,

objc_sync_enter / objc_sync_exit不是一个好主意。

答案 10 :(得分:7)

在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对象发送解锁消息时,必须确保从发送初始锁定消息的同一线程发送消息。从另一个线程解锁锁定可能导致未定义的行为。

答案 11 :(得分:4)

我刚刚在2018年WWDC的“了解崩溃和崩溃日志” session 414中找到了答案。 正如conmulligan指出的,正确的方法应该是使用具有同步功能的DispatchQueues。

迅速4中的内容应类似于以下内容:

class ImageCache {
    private let queue = DispatchQueue(label: "sync queue")
    private var storage: [String: UIImage] = [:]
    public subscript(key: String) -> UIImage? {
        get {
          return queue.sync {
            return storage[key]
          }
        }
        set {
          queue.sync {
            storage[key] = newValue
          }
        }
    }
}

与障碍并发,读取是异步的,写入等待先前的请求。

class ImageCache {
    private let queue = DispatchQueue(label: "with barriers", attributes: .concurrent)
    private var storage: [String: UIImage] = [:]

    func get(_ key: String, onResult: @escaping (UIImage?)->Void) {
        queue.async { [weak self] in
            guard let self = self else { return }
            onResult(self.storage[key])
        }
    }

    func set(_ image: UIImage, for key: String) {
        queue.async(flags: .barrier) { [weak self] in
            guard let self = self else { return }
            self.storage[key] = image
        }
    }
}

答案 12 :(得分:3)

使用Swift的属性包装器,这就是我现在正在使用的内容:

@propertyWrapper public struct NCCSerialized<Wrapped> {
    private let queue = DispatchQueue(label: "com.nuclearcyborg.NCCSerialized_\(UUID().uuidString)")

    private var _wrappedValue: Wrapped
    public var wrappedValue: Wrapped {
        get { queue.sync { _wrappedValue } }
        set { queue.sync { _wrappedValue = newValue } }
    }

    public init(wrappedValue: Wrapped) {
        self._wrappedValue = wrappedValue
    }
}

然后您可以执行以下操作:

@NCCSerialized var foo: Int = 10

@NCCSerialized var myData: [SomeStruct] = []

然后像往常一样访问变量。

答案 13 :(得分:2)

尝试:NSRecursiveLock

  

一个锁可以被同一线程多次获取而没有   导致死锁。

let lock = NSRecursiveLock()

func f() {
    lock.lock()
    //Your Code
    lock.unlock()
}

func f2() {
    lock.lock()
    defer {
        lock.unlock()
    }
    //Your Code
}

答案 14 :(得分:1)

总之,这里提供了更常见的方法,包括返回值或void,以及抛出

导入基金会

extension NSObject {


    func synchronized<T>(lockObj: AnyObject!, closure: () throws -> T) rethrows ->  T
    {
        objc_sync_enter(lockObj)
        defer {
            objc_sync_exit(lockObj)
        }

        return try closure()
    }


}

答案 15 :(得分:1)

详细

xCode 8.3.1,swift 3.1

任务

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

代码

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

答案 16 :(得分:0)

dispatch_barrier_async是更好的方法,但不阻止当前线程。

dispatch_barrier_async(accessQueue,{                         字典[object.ID] =对象                 })

答案 17 :(得分:0)

Based on ɲ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

答案 18 :(得分:0)

图我将发布基于先前答案的Swift 5实现。谢谢你们!我发现也有一个返回值很有帮助,所以我有两种方法。

这是第一个简单的课程:

import Foundation
class Sync {
public class func synced(_ lock: Any, closure: () -> ()) {
        objc_sync_enter(lock)
        defer { objc_sync_exit(lock) }
        closure()
    }
    public class func syncedReturn(_ lock: Any, closure: () -> (Any?)) -> Any? {
        objc_sync_enter(lock)
        defer { objc_sync_exit(lock) }
        return closure()
    }
}

然后使用它,如需要返回值:

return Sync.syncedReturn(self, closure: {
    // some code here
    return "hello world"
})

或者:

Sync.synced(self, closure: {
    // do some work synchronously
})

答案 19 :(得分:0)

为什么要锁定困难和麻烦? 使用调度障碍。

调度屏障在并发队列中创建同步点。

在它运行时,队列中的其他块都不允许运行,即使它是并发的和其他核心可用。

如果这听起来像一个独占(写)锁,它就是。 非障碍块可以被认为是共享(读)锁。

只要通过队列执行对资源的所有访问,障碍就会提供非常便宜的同步。

答案 20 :(得分:-3)

另一种方法是创建一个超类然后继承它。这样你可以更直接地使用GCD

class Lockable {
    let lockableQ:dispatch_queue_t

    init() {
        lockableQ = dispatch_queue_create("com.blah.blah.\(self.dynamicType)", DISPATCH_QUEUE_SERIAL)
    }

    func lock(closure: () -> ()) {
        dispatch_sync(lockableQ, closure)
    }
}


class Foo: Lockable {

    func boo() {
        lock {
            ....... do something
        }
    }