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

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

Ответ 1

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

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

Ответ 2

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

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

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!

Ответ 3

Мне нравятся и используют многие ответы здесь, поэтому я бы выбрал то, что лучше всего подходит для вас. Тем не менее, метод, который я предпочитаю, когда мне нужно что-то вроде objective-c @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 statement выполняются независимо от того, как передается программный контроль". 1

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

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

Ответ 8

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

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

    try block()
}

Ответ 9

Swift 3

Этот код имеет возможность повторного входа и может работать с асинхронными вызовами функций. В этом коде, после вызова someAsyncFunc(), другое закрытие функции в последовательной очереди будет обрабатываться, но будет заблокировано с помощью semaphore.wait(), пока не будет вызван сигнал(). 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

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

Ответ 11

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

В Swift 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

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

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


}

Ответ 13

Подробнее

xКод 8.3.1, быстрый 3.1

Task

Чтение значения записи из разных потоков (async).

код

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
}

Полный образец

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

Ответ 14

Рисунок Я опубликую свою реализацию 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
})

Ответ 15

Попробуйте: NSRecursiveLock

Блокировка, которая может быть получена несколько раз одним потоком без вызывая тупик.

let lock = NSRecursiveLock()

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

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

Ответ 16

Почему это затрудняет и затрудняет блокировки? Используйте Dispatch Barriers.

С помощью диспетчерского барьера создается точка синхронизации в параллельной очереди.

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

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

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

Ответ 17

Основываясь на ɲ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

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

dispatch_barrier_async (accessQueue, {                       словарь [object.ID] = объект               })

Ответ 19

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