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 может иметь произвольный тип возврата и nicerethrowsповедение в 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