Swift kitabını aradım, ancak @synchronized'in Swift sürümünü bulamıyorum. Swift'te karşılıklı dışlamayı nasıl yaparım?
removeFirst()
?
Swift kitabını aradım, ancak @synchronized'in Swift sürümünü bulamıyorum. Swift'te karşılıklı dışlamayı nasıl yaparım?
removeFirst()
?
Yanıtlar:
GCD kullanabilirsiniz. Bundan biraz daha ayrıntılı @synchronized
, ancak bir yedek olarak çalışıyor:
let serialQueue = DispatchQueue(label: "com.test.mySerialQueue")
serialQueue.sync {
// code
}
Bunu kendim arıyordum ve henüz bunun için hızlı bir iç yapı yok.
Matt Bridges ve diğerlerinden gördüğüm bazı kodlara dayanarak bu küçük yardımcı işlevi yaptım.
func synced(_ lock: Any, closure: () -> ()) {
objc_sync_enter(lock)
closure()
objc_sync_exit(lock)
}
Kullanımı oldukça basit
synced(self) {
println("This is a synchronized closure")
}
Bununla ilgili bulduğum bir sorun var. Bir diziyi kilit argümanı olarak iletmek, bu noktada çok geniş bir derleyici hatasına neden oluyor gibi görünüyor. Aksi halde istendiği gibi çalışıyor gibi görünüyor.
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
güzel bloğu, ama not o gibi gerçek yerleşik blok deyimi ile aynı olmadığını @synchronized
, çünkü Objective-C blok return
ve break
benzeri çevreleyen fonksiyonu / döngü atlamaya artık çalışmaz ifadeleri bu sıradan bir ifade olsaydı olurdu.
defer
sağlamak için yeni anahtar kelime kullanmak için harika bir yer olurdu . objc_sync_exit
closure
Buradaki cevapların çoğunu seviyorum ve kullanıyorum, bu yüzden sizin için en uygun olanı seçerim. Bununla birlikte, obj-c gibi bir şeye ihtiyacım olduğunda tercih ettiğim yöntem , hızlı 2'de sunulan ifadeyi @synchronized
kullanır defer
.
{
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
//
// code of critical section goes here
//
} // <-- lock released when this block is exited
Bu yöntem hakkında güzel bir şey, kritik bölüm istenilen herhangi bir şekilde içeren blok çıkabilirsiniz olmasıdır (örneğin return
, break
, continue
, throw
) ve "erteleme ifadesi içinde ifadeleri program kontrol aktarılır nasıl olursa olsun yürütülür." 1
lock
? Nasıl lock
başlatılır?
lock
herhangi bir objektif-c nesnesidir.
Sen arasındaki ifadeleri sandviç olabilir objc_sync_enter(obj: AnyObject?)
ve objc_sync_exit(obj: AnyObject?)
. @Synchronized anahtar sözcüğü kapakların altındaki bu yöntemleri kullanıyor. yani
objc_sync_enter(self)
... synchronized code ...
objc_sync_exit(self)
objc_sync_enter
ve objc_sync_exit
Objc-sync.h dosyasında tanımlanan ve açık kaynak yöntemlerdir: opensource.apple.com/source/objc4/objc4-371.2/runtime/…
objc_sync_enter(…)
ve objc_sync_exit(…)
vb iOS / MacOS / sağladığı kamu başlıkları bulunmaktadır. API'lar ( ….sdk
yolun içindeymiş gibi görünüyor usr/include/objc/objc-sync.h
) . Bir şeyin herkese açık bir API olup olmadığını öğrenmenin en kolay yolu (Xcode'da) işlev adını yazmaktır (örneğin objc_sync_enter()
; C işlevleri için bağımsız değişkenlerin belirtilmesi gerekmez) , sonra komut tıklatmayı deneyin. Bu API'nın başlık dosyasını gösterirse, iyidir (başlık herkese açık olmasaydı başlığı göremezsiniz) .
@synchronized
Objective-C yönergesinin analogu, rethrows
Swift'te keyfi bir dönüş tipi ve hoş bir davranışa sahip olabilir.
// Swift 3
func synchronized<T>(_ lock: AnyObject, _ body: () throws -> T) rethrows -> T {
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
return try body()
}
İfadenin kullanılması, defer
geçici bir değişken girmeden doğrudan bir değer döndürmenizi sağlar.
Swift 2'de, @noescape
daha fazla optimizasyona izin vermek için özelliği kapağa ekleyin :
// 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] (rasgele dönüş tipini sevdiğim) ve Tod Cunningham [2] (sevdiğim yer defer
) yanıtlarına dayanarak .
SWIFT 4
Swift 4'te kaynakları kilitlemek için GCD dağıtım kuyruklarını kullanabilirsiniz.
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
kullanılamıyor gibi görünüyor. Ancak .concurrent
kullanılabilir. : /
myObject.state = myObject.state + 1
aynı anda çalışırsanız , toplam işlemleri saymaz, bunun yerine belirsiz olmayan bir değer verir. Bu sorunu çözmek için, arama kodu seri bir sıraya sarılmalıdır, böylece hem okuma hem de yazma atomik olarak gerçekleşir. Tabii ki Obj-c'lerin @synchronised
de aynı sorunu var, bu yüzden uygulamanız doğrudur.
myObject.state += 1
okuma, sonra yazma işlemlerinin birleşimidir. Başka bir iş parçacığı hala bir değer ayarlamak / yazmak için arasına girebilir. Gereğince objc.io/blog/2018/12/18/atomic-variables , yayınlanmaya kolay olurdu set
kendisi değişken altında yerine ve bir senkron blok / kapanmasında.
Dönüş işlevselliği eklemek için şunları yapabilirsiniz:
func synchronize<T>(lockObj: AnyObject!, closure: ()->T) -> T
{
objc_sync_enter(lockObj)
var retVal: T = closure()
objc_sync_exit(lockObj)
return retVal
}
Daha sonra, şunu kullanarak arayabilirsiniz:
func importantMethod(...) -> Bool {
return synchronize(self) {
if(feelLikeReturningTrue) { return true }
// do other things
if(feelLikeReturningTrueNow) { return true }
// more things
return whatIFeelLike ? true : false
}
}
Bryan McLemore cevabını kullanarak, Swift 2.0 erteleme yeteneğiyle güvenli bir malikaneye atılan nesneleri desteklemek için uzattım.
func synchronized( lock:AnyObject, block:() throws -> Void ) rethrows
{
objc_sync_enter(lock)
defer {
objc_sync_exit(lock)
}
try block()
}
Hızlı 3
Bu kod yeniden giriş özelliğine sahiptir ve Asenkron işlev çağrılarıyla çalışabilir. Bu kodda, someAsyncFunc () çağrıldıktan sonra, seri sıradaki başka bir işlev kapanışı işlenir ancak signal () çağrılıncaya kadar semaphore.wait () tarafından engellenir. internalQueue.sync, yanılmıyorsam ana iş parçacığını engelleyeceği için kullanılmamalıdır.
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 hata işlemeden iyi bir fikir değildir.
2018 WWDC'nin "Çökmeleri ve Çökme Günlüklerini Anlama" oturumunda 414 oturumunda senkronize edilmiş DispatchQueues kullanarak aşağıdaki yolu gösterirler.
Hızlı 4'te aşağıdaki gibi bir şey olmalıdır:
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
}
}
}
}
Her neyse, engelli eşzamanlı kuyrukları kullanarak daha hızlı okuma yapabilirsiniz. Senkronizasyon ve zaman uyumsuz okumalar eş zamanlı olarak gerçekleştirilir ve önceki işlemlerin tamamlanmasını yeni bir değer yazmak bekler.
class ImageCache {
private let queue = DispatchQueue(label: "with barriers", attributes: .concurrent)
private var storage: [String: UIImage] = [:]
func get(_ key: String) -> UIImage? {
return queue.sync { [weak self] in
guard let self = self else { return nil }
return 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
}
}
}
Swift4'te NSLock kullanın :
let lock = NSLock()
lock.lock()
if isRunning == true {
print("Service IS running ==> please wait")
return
} else {
print("Service not running")
}
isRunning = true
lock.unlock()
Uyarı NSLock sınıfı, kilitleme davranışını uygulamak için POSIX iş parçacıklarını kullanır. Bir NSLock nesnesine kilit açma mesajı gönderirken, mesajın ilk kilit mesajını gönderen aynı iş parçacığından gönderildiğinden emin olmalısınız. Farklı bir iş parçacığından bir kilidin kilidinin açılması, tanımlanmamış davranışa neden olabilir.
Modern Swift 5'te, geri dönüş özelliği ile:
/**
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()
}
Dönüş değeri özelliğinden yararlanmak için bu şekilde kullanın:
let returnedValue = synchronized(self) {
// Your code here
return yourCode()
}
Ya da aksi takdirde:
synchronized(self) {
// Your code here
yourCode()
}
GCD
). Aslında hiç kimse nasıl kullanılacağını ya da nasıl kullanılacağını anlamıyor Thread
. Ben bundan memnunum - oysa GCD
gotchas ve sınırlamalarla doludur.
Deneyin: NSRecursiveLock
Bir kilitlenmeye neden olmadan aynı iş parçacığı tarafından birden çok kez alınabilen bir kilit.
let lock = NSRecursiveLock()
func f() {
lock.lock()
//Your Code
lock.unlock()
}
func f2() {
lock.lock()
defer {
lock.unlock()
}
//Your Code
}
Şekil Swift 5 uygulamasını önceki cevaplardan yola çıkarak yayınlayacağım. Teşekkürler beyler! Ben de bir değer döndüren bir tane için yararlı buldum, bu yüzden iki yöntem var.
İlk yapmak için basit bir sınıf:
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()
}
}
Ardından, bir dönüş değerine ihtiyaç duyuyorsanız bu şekilde kullanın:
return Sync.syncedReturn(self, closure: {
// some code here
return "hello world"
})
Veya:
Sync.synced(self, closure: {
// do some work synchronously
})
public class func synced<T>(_ lock: Any, closure: () -> T)
, hem geçersizdir, hem de geçersizdir. Ayrıca yeniden regrows şeyler var.
xCode 8.3.1, hızlı 3.1
Farklı evrelerden (async) yazma değerini okuyun.
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
}
uzantı 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()
}
}
}
sınıf 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)")
}
}
}
Swift'in mülk sarmalayıcıları ile şu an kullandığım şey:
@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
}
}
Sonra sadece şunları yapabilirsiniz:
@NCCSerialized var foo: Int = 10
veya
@NCCSerialized var myData: [SomeStruct] = []
Ardından değişkene normalde yaptığınız gibi erişin.
DispatchQueue
yapan kullanıcıdan gizli bir yaratma yan etkisi vardır . Fikrimi rahatlatmak için bu SO referansını buldum: stackoverflow.com/a/35022486/1060314
Sonuç olarak, Burada dönüş değeri veya geçersizliği içeren daha yaygın bir yol verin ve atın
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()
}
}
Neden kilitleri zorlaştırıyor ve güçlük çekiyorsunuz? Dağıtım Engelleri Kullanın.
Bir dağıtım engeli, eşzamanlı bir kuyruk içinde bir senkronizasyon noktası oluşturur.
Çalışırken, eşzamanlı ve diğer çekirdekler mevcut olsa bile kuyruktaki başka bir bloğun çalışmasına izin verilmez.
Bu özel (yazma) bir kilit gibi geliyorsa, öyle. Bariyer olmayan bloklar paylaşılan (okuma) kilitler olarak düşünülebilir.
Kaynağa tüm erişim kuyruk üzerinden gerçekleştirildiği sürece, engeller çok ucuz senkronizasyon sağlar.
ɳuroburɳ temel alınarak bir alt sınıf durumu test edin
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
Başka bir yöntem, bir üst sınıf oluşturmak ve daha sonra devralmaktır. Bu şekilde GCD'yi daha doğrudan kullanabilirsiniz
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
}
}