Что такое Swift эквивалент Objective-C "@synchronized"?



Я искал Swift книгу, но не могу найти Swift версию @synchronized. Как сделать взаимное исключение в Swift?

700   17  

17 ответов:

используйте GCD. Это немного более многословно, чем @synchronized, но отлично работает в качестве замены:

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

Я искал это сам и пришел к выводу, что внутри swift еще нет родной конструкции для этого.

Я сделал эту небольшую вспомогательную функцию, основанную на некоторых из кода, который я видел от Мэтта Бриджеса и других.

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

использование довольно прямо вперед

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

есть одна проблема, которую я нашел с этим. Передача массива в качестве аргумента блокировки, по-видимому, вызывает очень тупую ошибку компилятора на этом этапе. В противном случае, хотя это, кажется, работает по желанию.

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!

мне нравится и использовать многие из ответов здесь, так что я бы выбрал то, что лучше всего работает для вас. Так сказать, метод, который я предпочитаю, когда мне нужно что-то вроде цели-с @synchronized использует defer заявление введено в swift 2.

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

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

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

хорошая вещь об этом методе, заключается в том, что ваш критический раздел может выйти из содержащего блока любым желаемым способом (например,return,break,continue,throw), и " выполняются операторы внутри оператора defer независимо от того, как передается управление программой."1

вы можете сэндвич заявления между objc_sync_enter(obj: AnyObject?) и objc_sync_exit(obj: AnyObject?). Ключевое слово @synchronized использует эти методы под обложками. то есть

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

аналог @synchronized директива от Objective-C может иметь произвольный тип возврата и nice rethrows поведение в 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] (где мне нравится произвольный тип возврата) и Тод Каннингем [2] (где мне нравится defer).

SWIFT 4

в Swift 4 Вы можете использовать очереди отправки GCDs для блокировки ресурсов.

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

используя ответ Брайана Маклемора, я расширил его для поддержки объектов, которые бросают в безопасное поместье с возможностью отсрочки Swift 2.0.

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

    try block()
}

чтобы добавить функцию возврата, вы можете сделать это:

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

Swift 3

этот код имеет возможность повторного ввода и может работать с асинхронными вызовами функций. В этом коде после вызова функции someAsyncFunc () будет обработано другое закрытие функции в последовательной очереди, но будет заблокировано семафором.подождите (), пока не будет вызван сигнал (). внутренняя очередь.синхронизация не должна использоваться, поскольку она заблокирует основной поток, если я не ошибаюсь.

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 не очень хорошая идея без ошибок обращение.

использовать NSLock в Swift4:

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 необходимо убедиться, что сообщение отправляется из того же потока, который отправил начальное сообщение блокировки. Разблокирование замка из другого потока может привести к неопределенному поведению.

Я только что нашел ответ в разделе "понимание сбоев и журналов сбоев"сессии 414 из WWDC 2018. Как отметил конмаллиган, правильным способом должно быть использование DispatchQueues с синхронизацией.

в Swift 4 должно быть что-то вроде следующего:

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

В заключение, здесь дают более распространенный способ, который включает возвращаемое значение или void, и throw

import Foundation

extension NSObject {


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

        return try closure()
    }


}

зачем усложнять и хлопотать с замками? Используйте Барьеры Отправки.

барьер отправки создает точку синхронизации в параллельной очереди.

Пока он работает, ни один другой блок в очереди не разрешается запускать, даже если он параллельный и доступны другие ядра.

Если это звучит как эксклюзивная (запись) блокировка, это так. Небарьерные блоки можно рассматривать как общие (читаемые) блокировки.

Покуда весь доступ к ресурс осуществляется через очередь, барьеры обеспечивают очень дешевую синхронизацию.

на основе ɲ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

dispatch_barrier_async является лучшим способом, не блокируя текущий поток.

dispatch_barrier_async(accessQueue, { словарь[объект.Идентификатор] = объект })

подробности

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

другой метод-создать суперкласс и затем наследовать его. Таким образом, вы можете использовать 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
        }
    }

Comments

    Ничего не найдено.