ฉันค้นหาหนังสือ Swift แต่ไม่พบ @synchronized รุ่น Swift ฉันจะยกเว้นแบบรวมใน Swift ได้อย่างไร
removeFirst()
?
ฉันค้นหาหนังสือ Swift แต่ไม่พบ @synchronized รุ่น Swift ฉันจะยกเว้นแบบรวมใน Swift ได้อย่างไร
removeFirst()
?
คำตอบ:
คุณสามารถใช้ GCD มันเป็น verbose มากกว่าเล็กน้อย@synchronized
แต่ทำงานแทน:
let serialQueue = DispatchQueue(label: "com.test.mySerialQueue")
serialQueue.sync {
// 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
บล็อกไว้อย่างดี แต่โปรดทราบว่ามันไม่เหมือนกับคำสั่งบล็อก builtin จริงเช่น@synchronized
บล็อกใน Objective-C เนื่องจากreturn
และbreak
คำสั่งไม่ทำงานอีกต่อไปที่จะกระโดดออกจากฟังก์ชั่น / วงรอบเช่น มันจะถ้านี่เป็นคำสั่งธรรมดา
defer
คำหลักใหม่เพื่อให้แน่ใจว่าobjc_sync_exit
ได้รับการเรียกแม้ว่าจะclosure
พ่น
ฉันชอบและใช้คำตอบมากมายที่นี่ดังนั้นฉันจะเลือกแบบใดที่ดีที่สุดสำหรับคุณ ที่กล่าวว่าวิธีที่ฉันชอบเมื่อฉันต้องการบางสิ่งบางอย่างเช่นวัตถุประสงค์ -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
) และ "งบภายในคำสั่งเลื่อนจะดำเนินการว่าโปรแกรมควบคุมจะถูกโอน no." 1
lock
อะไร จะlock
เริ่มต้นอย่างไร
lock
เป็นวัตถุ -c ใด ๆ
คุณสามารถ sandwich งบระหว่างและobjc_sync_enter(obj: AnyObject?)
objc_sync_exit(obj: AnyObject?)
คีย์เวิร์ด @synchronized กำลังใช้วิธีเหล่านั้นภายใต้หน้าปก กล่าวคือ
objc_sync_enter(self)
... synchronized code ...
objc_sync_exit(self)
objc_sync_enter
และobjc_sync_exit
เป็นวิธีที่กำหนดไว้ใน Objc-sync.h และเป็นโอเพ่นซอร์ส: opensource.apple.com/source/objc4/objc4-371.2/runtime/…
objc_sync_enter(…)
และobjc_sync_exit(…)
เป็นส่วนหัวสาธารณะที่ได้รับจาก iOS / macOS / ฯลฯ APIs (ลักษณะเหมือนพวกเขากำลังอยู่ภายใน….sdk
ที่เส้นทางusr/include/objc/objc-sync.h
) วิธีที่ง่ายที่สุดในการตรวจสอบว่ามีบางอย่างที่เป็นสาธารณะ API หรือไม่คือ(ใน Xcode)พิมพ์ชื่อฟังก์ชัน(เช่นobjc_sync_enter()
ไม่จำเป็นต้องระบุอาร์กิวเมนต์สำหรับฟังก์ชัน C)จากนั้นลองคลิกคำสั่ง ถ้ามันแสดงให้คุณเห็นไฟล์ส่วนหัวสำหรับ API นั้นแล้วคุณจะดี(เนื่องจากคุณจะไม่สามารถมองเห็นส่วนหัวถ้ามันไม่ได้สาธารณะ)
อะนาล็อกของ@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
)
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 }
}
}
}
.serial
ดูเหมือนว่าจะไม่พร้อมใช้งาน แต่.concurrent
สามารถใช้ได้ : /
myObject.state = myObject.state + 1
พร้อมกันมันจะไม่นับการดำเนินการทั้งหมด แต่จะให้ค่า nondeterministic แทน เพื่อแก้ปัญหานั้นรหัสการโทรควรถูกห่อในคิวอนุกรมเพื่อให้ทั้งการอ่านและการเขียนเกิดขึ้นแบบอะตอม แน่นอนว่า Obj-c @synchronised
มีปัญหาเดียวกันดังนั้นในแง่ที่ว่าการติดตั้งของคุณนั้นถูกต้อง
myObject.state += 1
คือการรวมกันของการอ่านแล้วการดำเนินการเขียน เธรดอื่น ๆ ยังสามารถเข้ามาเพื่อตั้งค่า / เขียนค่า ตามobjc.io/blog/2018/12/18/atomic-variablesคุณจะสามารถเรียกใช้set
บล็อกซิงค์ / ปิดในซิงค์ได้ง่ายกว่าแทนและไม่อยู่ภายใต้ตัวแปรนั้น
ในการเพิ่มฟังก์ชันการคืนสินค้าคุณสามารถทำสิ่งนี้ได้:
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
}
}
ด้วยการใช้คำตอบของ Bryan McLemore ฉันได้ขยายมันเพื่อรองรับวัตถุที่ขว้างคฤหาสน์ที่ปลอดภัยด้วยความสามารถในการเลื่อนที่รวดเร็วของ Swift 2.0
func synchronized( lock:AnyObject, block:() throws -> Void ) rethrows
{
objc_sync_enter(lock)
defer {
objc_sync_exit(lock)
}
try block()
}
rethrows
เพื่อให้ง่ายต่อการใช้งานด้วยการปิดไม่ใช่การขว้างปา (จำเป็นต้องใช้ไม่ได้try
) ตามที่ปรากฏในคำตอบของฉัน
สวิฟท์ 3
รหัสนี้มีความสามารถในการเข้าใหม่และสามารถทำงานกับการเรียกใช้ฟังก์ชัน Asynchronous ในรหัสนี้หลังจาก 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 ไม่ใช่ความคิดที่ดีหากไม่มีการจัดการข้อผิดพลาด
ในเซสชั่น "การทำความเข้าใจข้อขัดข้องและบันทึกข้อขัดข้อง" 414ของ 2018 WWDC พวกเขาแสดงวิธีดังต่อไปนี้โดยใช้ 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) -> 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
}
}
}
ใช้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 คุณต้องแน่ใจว่าข้อความนั้นถูกส่งจากเธรดเดียวกันที่ส่งข้อความล็อกเริ่มต้น การปลดล็อคการล็อคจากเธรดอื่นอาจส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนด
ใน Swift 5 ที่ทันสมัยพร้อมความสามารถในการส่งคืน:
/**
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()
}
ใช้อย่างนี้เพื่อใช้ประโยชน์จากความสามารถในการคืนค่า:
let returnedValue = synchronized(self) {
// Your code here
return yourCode()
}
หรือเช่นนั้น:
synchronized(self) {
// Your code here
yourCode()
}
GCD
) ดูเหมือนว่าไม่มีใครใช้หรือเข้าใจวิธีใช้เป็นThread
หลัก ฉันมีความสุขกับมัน - ในขณะที่GCD
เต็มไปด้วย gotchas และข้อ จำกัด
ลอง: NSRecursiveLock
การล็อกที่อาจได้มาหลายครั้งโดยเธรดเดียวกันโดยไม่ทำให้เกิดการหยุดชะงัก
let lock = NSRecursiveLock()
func f() {
lock.lock()
//Your Code
lock.unlock()
}
func f2() {
lock.lock()
defer {
lock.unlock()
}
//Your Code
}
รูปที่ฉันจะโพสต์การนำไปใช้งาน 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
})
public class func synced<T>(_ lock: Any, closure: () -> T)
งานได้ทั้งเป็นโมฆะและแบบอื่น ๆ นอกจากนี้ยังมีสิ่งปลูก
xCode 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
}
ส่วนขยาย 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)")
}
}
}
ด้วยการห่อคุณสมบัติของ Swift นี่คือสิ่งที่ฉันใช้ตอนนี้:
@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
}
}
จากนั้นคุณสามารถทำได้:
@NCCSerialized var foo: Int = 10
หรือ
@NCCSerialized var myData: [SomeStruct] = []
จากนั้นเข้าถึงตัวแปรตามปกติ
DispatchQueue
ที่ถูกซ่อนไว้จากผู้ใช้ ฉันพบการอ้างอิงดังนั้นเพื่อทำให้ฉันสบายใจ: stackoverflow.com/a/35022486/1060314
โดยสรุปนี่คือวิธีทั่วไปที่รวมถึงมูลค่าส่งคืนหรือเป็นโมฆะและโยน
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()
}
}
ทำไมจึงทำให้ล็อคและยุ่งยากยุ่งยาก? ใช้อุปสรรคการจัดส่ง
สิ่งกีดขวางการจัดส่งสร้างจุดประสานภายในคิวพร้อมกัน
ขณะที่กำลังทำงานไม่มีการอนุญาตให้บล็อกอื่นในคิวทำงานแม้ว่าจะพร้อมกันและมีแกนอื่น ๆ ก็ตาม
หากฟังดูเหมือนล็อคแบบเอกสิทธิ์ (เขียน) แสดงว่าเป็น บล็อกที่ไม่มีสิ่งกีดขวางสามารถถูกคิดว่าเป็นการล็อกแบบแบ่งใช้ (อ่าน)
ตราบใดที่การเข้าถึงทรัพยากรทั้งหมดถูกดำเนินการผ่านคิวอุปสรรคจะให้การซิงโครไนซ์ราคาถูกมาก
จากaneuroburɳทดสอบตัวพิมพ์เล็ก
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, {dictionary [object.ID] = object})
อีกวิธีหนึ่งคือการสร้างซูเปอร์คลาสจากนั้นสืบทอด วิธีนี้คุณสามารถใช้ 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
}
}