Я искал книгу Swift, но не могу найти Swift-версию @synchronized. Как сделать взаимное исключение в Swift?
Я использую GCD. Это немного более подробно, чем @synchronized
, но отлично работает как замена:
let lockQueue = dispatch_queue_create("com.test.LockQueue", nil)
dispatch_sync(lockQueue) {
// code
}
Я искал это сам и пришел к выводу, что внутри этого не существует встроенной встроенной конструкции.
Я сделал эту небольшую вспомогательную функцию, основанную на некотором коде, который я видел у 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!
@synchronized
, но обратите внимание, что он не идентичен реальному встроенному выражению блока, @synchronized
блок @synchronized
в Objective-C, потому что операторы return
и break
больше не работают, чтобы выпрыгнуть из окружающая функция / цикл, как если бы это было обычное утверждение.
Мне нравятся и используют многие ответы здесь, поэтому я бы выбрал то, что лучше всего подходит для вас. Тем не менее, метод, который я предпочитаю, когда мне нужно что-то вроде 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
lock
? Как инициализируется lock
?
Вы можете сэндвич-выражениями между objc_sync_enter(obj: AnyObject?)
и objc_sync_exit(obj: AnyObject?)
. Ключевое слово @synchronized использует эти методы под обложками. то есть.
objc_sync_enter(self)
... synchronized code ...
objc_sync_exit(self)
Аналог директивы @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
).
Чтобы добавить функцию возврата, вы можете сделать это:
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 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 }
}
}
}
.serial
кажется, недоступен. Но .concurrent
доступен. : /
Используя ответ Брайана МакЛемора, я продлил его, чтобы поддерживать объекты, которые бросают надежную усадьбу с возможностью замедления Swift 2.0.
func synchronized( lock:AnyObject, block:() throws -> Void ) rethrows
{
objc_sync_enter(lock)
defer {
objc_sync_exit(lock)
}
try block()
}
rethrows
чтобы упростить использование с замыканиями без бросков (не нужно использовать try
).
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 не является хорошей идеей без обработки ошибок.
Используйте 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 вы должны быть уверены, что сообщение отправлено из того же потока, который отправил исходное сообщение блокировки. Разблокировка блокировки из другого потока может привести к поведению undefined.
xКод 8.3.1, быстрый 3.1
Чтение значения записи из разных потоков (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)")
}
}
}
В заключение здесь приведены более распространенные способы, которые включают возвращаемое значение или 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()
}
}
dispatch_barrier_async - лучший способ, не блокируя текущий поток.
dispatch_barrier_async (accessQueue, { словарь [object.ID] = объект })
Основываясь на ɲ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 Barriers.
С помощью диспетчерского барьера создается точка синхронизации в параллельной очереди.
При его запуске ни один другой блок в очереди не может работать, даже если его параллельные и другие ядра доступны.
Если это звучит как блокировка (запись), это так. Небарьерные блоки можно рассматривать как общие (прочитанные) блокировки.
Пока весь доступ к ресурсу выполняется через очередь, барьеры обеспечивают очень дешевую синхронизацию.
Другой метод - создать суперкласс и затем наследовать его. Таким образом, вы можете напрямую использовать 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
}
}
removeFirst()
?