Swift เทียบเท่ากับ Objective-C ของ“ @synchronized” คืออะไร?


231

ฉันค้นหาหนังสือ Swift แต่ไม่พบ @synchronized รุ่น Swift ฉันจะยกเว้นแบบรวมใน Swift ได้อย่างไร


1
ฉันจะใช้สิ่งกีดขวางการจัดส่ง ปัญหาและอุปสรรคที่มีการประสานราคาถูกมาก dispatch_barrier_async () ฯลฯ
Frederick C. Lee

@ FrederickC.Lee สิ่งที่ถ้าคุณจำเป็นต้องมีการเขียนในการทำข้อมูลให้ตรงกันแม้ว่าเช่นเมื่อมีการสร้างเสื้อคลุมสำหรับremoveFirst()?
ScottyBlades

คำตอบ:


183

คุณสามารถใช้ GCD มันเป็น verbose มากกว่าเล็กน้อย@synchronizedแต่ทำงานแทน:

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

12
นี่เป็นสิ่งที่ดี แต่ขาดความสามารถในการเข้าใหม่ที่คุณมีด้วย @synchronized
Michael Waterfall

9
ด้วยวิธีการนี้คุณจะต้องระมัดระวัง บล็อกของคุณอาจถูกเรียกใช้งานบนเธรดอื่น API เอกสารพูดว่า: "ในฐานะที่เป็นการเพิ่มประสิทธิภาพฟังก์ชั่นนี้จะเรียกบล็อกบนเธรดปัจจุบันเมื่อเป็นไปได้"
ไบโอ

20
บทความที่ยอดเยี่ยมจาก Matt Gallagher เกี่ยวกับเรื่องนี้: cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html
wuf810

4
ไม่นี่เป็นสาเหตุทำให้เกิดการหยุดชะงักเป็นครั้งคราว
Tom Kraina

70
ไม่ไม่และไม่ ลองดี แต่ใช้งานได้ไม่สมบูรณ์ ทำไม? การอ่านที่จำเป็น (การเปรียบเทียบทางเลือกข้อควรระวัง) และกรอบอรรถประโยชน์ที่ยอดเยี่ยมจาก Matt Gallagher ที่นี่: cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html @ wuf810 พูดถึงเรื่องนี้เป็นครั้งแรก (HT) understated บทความนี้ดีเพียงใด ทุกคนควรอ่าน (โปรด
อัปโหลด

181

ฉันกำลังมองหาสิ่งนี้ด้วยตัวเองและมาถึงข้อสรุปที่ว่ายังไม่มีสิ่งปลูกสร้างภายในที่รวดเร็วสำหรับสิ่งนี้

ฉันสร้างฟังก์ชันผู้ช่วยตัวเล็กนี้โดยอ้างอิงจากโค้ดบางส่วนที่ฉันเห็นจาก 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!

ดี! โปรดแจ้งจุดบกพร่องหากยังคงมีปัญหาใน 1.0
MattD

14
สิ่งนี้มีประโยชน์มากและเก็บรักษาไวยากรณ์ของ@synchronizedบล็อกไว้อย่างดี แต่โปรดทราบว่ามันไม่เหมือนกับคำสั่งบล็อก builtin จริงเช่น@synchronizedบล็อกใน Objective-C เนื่องจากreturnและbreakคำสั่งไม่ทำงานอีกต่อไปที่จะกระโดดออกจากฟังก์ชั่น / วงรอบเช่น มันจะถ้านี่เป็นคำสั่งธรรมดา
newacct

3
ข้อผิดพลาดน่าเป็นไปได้เนื่องจากอาร์เรย์นั้นถูกส่งผ่านเป็นค่าที่ไม่อ้างอิง
james_alvarez

9
นี่อาจเป็นสถานที่ที่ดีในการใช้deferคำหลักใหม่เพื่อให้แน่ใจว่าobjc_sync_exitได้รับการเรียกแม้ว่าจะclosureพ่น
devios1

3
@ t0rst การโทรคำตอบนี้ "มีข้อบกพร่อง" ซึ่งอ้างอิงจากบทความที่ลิงก์ไปยังไม่ถูกต้อง บทความกล่าวว่าวิธีนี้คือ "ช้ากว่าอุดมคติ" เล็กน้อยและ "จำกัด เฉพาะแพลตฟอร์มของ Apple" แต่นั่นไม่ได้ทำให้ "สมบูรณ์" โดยการยิงยาว
RenniePet

150

ฉันชอบและใช้คำตอบมากมายที่นี่ดังนั้นฉันจะเลือกแบบใดที่ดีที่สุดสำหรับคุณ ที่กล่าวว่าวิธีที่ฉันชอบเมื่อฉันต้องการบางสิ่งบางอย่างเช่นวัตถุประสงค์ -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


ฉันคิดว่านี่น่าจะเป็นทางออกที่หรูหราที่สุดที่นี่ ขอบคุณสำหรับความคิดเห็นของคุณ
Scott D

3
คือlockอะไร จะlockเริ่มต้นอย่างไร
Van Du Tran

6
lockเป็นวัตถุ -c ใด ๆ
uroeuroburɳ

1
ยอดเยี่ยม ฉันได้เขียนวิธีการล็อคบางอย่างเมื่อมีการแนะนำ Swift 1 และไม่ได้กลับมาเยี่ยมอีกครั้ง ลืมเรื่องการเลื่อนออกไปโดยสิ้นเชิง นี่คือวิธีที่จะไป!
แรนดี้

ฉันชอบสิ่งนี้ แต่ได้รับข้อผิดพลาดของคอมไพเลอร์ "Braced block of statement คือการปิดที่ไม่ได้ใช้" ใน Xcode 8 Ah ฉันเข้าใจแล้วว่าพวกเขาเป็นเพียงแค่วงเล็บปีกกาของฟังก์ชั่น
Duncan Groenewald

83

คุณสามารถ sandwich งบระหว่างและobjc_sync_enter(obj: AnyObject?) objc_sync_exit(obj: AnyObject?)คีย์เวิร์ด @synchronized กำลังใช้วิธีเหล่านั้นภายใต้หน้าปก กล่าวคือ

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

3
สิ่งนี้จะถูกพิจารณาว่าเป็นการใช้งาน API ส่วนตัวโดย Apple หรือไม่
Drux

2
ไม่objc_sync_enterและobjc_sync_exitเป็นวิธีที่กำหนดไว้ใน Objc-sync.h และเป็นโอเพ่นซอร์ส: opensource.apple.com/source/objc4/objc4-371.2/runtime/…
bontoJR

จะเกิดอะไรขึ้นถ้าหลายเธรดพยายามเข้าถึงทรัพยากรเดียวกันการรอครั้งที่สองลองอีกครั้งหรือล้มเหลว
TruMan1

เพิ่มสิ่งที่ @bontoJR พูดobjc_sync_enter(…)และobjc_sync_exit(…)เป็นส่วนหัวสาธารณะที่ได้รับจาก iOS / macOS / ฯลฯ APIs (ลักษณะเหมือนพวกเขากำลังอยู่ภายใน….sdkที่เส้นทางusr/include/objc/objc-sync.h ) วิธีที่ง่ายที่สุดในการตรวจสอบว่ามีบางอย่างที่เป็นสาธารณะ API หรือไม่คือ(ใน Xcode)พิมพ์ชื่อฟังก์ชัน(เช่นobjc_sync_enter()ไม่จำเป็นต้องระบุอาร์กิวเมนต์สำหรับฟังก์ชัน C)จากนั้นลองคลิกคำสั่ง ถ้ามันแสดงให้คุณเห็นไฟล์ส่วนหัวสำหรับ API นั้นแล้วคุณจะดี(เนื่องจากคุณจะไม่สามารถมองเห็นส่วนหัวถ้ามันไม่ได้สาธารณะ)
Slipp D. Thompson

75

อะนาล็อกของ@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)


Xcode กำลังบอกฉันว่า @noescape เป็นค่าเริ่มต้นและเลิกใช้ใน Swift 3
RenniePet

ถูกต้องรหัสในคำตอบนี้สำหรับ Swift 2 และต้องการการดัดแปลงสำหรับ Swift 3 ฉันจะอัปเดตเมื่อฉันมีเวลา
ดูแลระบบ

1
คุณอธิบายการใช้งานได้ไหม อาจมีตัวอย่าง .. ขอบคุณล่วงหน้า! ในกรณีของฉันฉันมีชุดที่ฉันต้องการซิงโครไนซ์เพราะฉันจัดการเนื้อหาใน DispatchQueue
sancho

@sancho ฉันต้องการให้โพสต์นี้กระชับ คุณดูเหมือนจะถามเกี่ยวกับแนวทางการเขียนโปรแกรมทั่วไปที่เกิดขึ้นพร้อมกันนั่นเป็นคำถามที่กว้างขวาง ลองถามเป็นคำถามแยกต่างหาก!
Werediver

41

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

ดูเหมือนจะไม่สามารถทำงานกับ XCode8.1 ได้ .serialดูเหมือนว่าจะไม่พร้อมใช้งาน แต่.concurrentสามารถใช้ได้ : /
Travis Griggs

2
ค่าเริ่มต้นคือ .serial
Duncan Groenewald

2
โปรดทราบว่ารูปแบบนี้ไม่ได้ป้องกันอย่างถูกต้องกับปัญหาหลายเธรดที่พบบ่อยที่สุด ตัวอย่างเช่นหากคุณทำงานmyObject.state = myObject.state + 1พร้อมกันมันจะไม่นับการดำเนินการทั้งหมด แต่จะให้ค่า nondeterministic แทน เพื่อแก้ปัญหานั้นรหัสการโทรควรถูกห่อในคิวอนุกรมเพื่อให้ทั้งการอ่านและการเขียนเกิดขึ้นแบบอะตอม แน่นอนว่า Obj-c @synchronisedมีปัญหาเดียวกันดังนั้นในแง่ที่ว่าการติดตั้งของคุณนั้นถูกต้อง
Berik

1
ใช่myObject.state += 1คือการรวมกันของการอ่านแล้วการดำเนินการเขียน เธรดอื่น ๆ ยังสามารถเข้ามาเพื่อตั้งค่า / เขียนค่า ตามobjc.io/blog/2018/12/18/atomic-variablesคุณจะสามารถเรียกใช้setบล็อกซิงค์ / ปิดในซิงค์ได้ง่ายกว่าแทนและไม่อยู่ภายใต้ตัวแปรนั้น
CyberMew

23

ในการเพิ่มฟังก์ชันการคืนสินค้าคุณสามารถทำสิ่งนี้ได้:

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

23

ด้วยการใช้คำตอบของ 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) ตามที่ปรากฏในคำตอบของฉัน
คนรับใช้

10

สวิฟท์ 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 ไม่ใช่ความคิดที่ดีหากไม่มีการจัดการข้อผิดพลาด


การจัดการข้อผิดพลาดอะไร คอมไพเลอร์จะไม่ยอมให้มีสิ่งใดเกิดขึ้น ในทางกลับกันหากคุณไม่ใช้ objc_sync_enter / exit คุณจะได้รับประสิทธิภาพที่เพิ่มขึ้น
gnasher729

8

ในเซสชั่น "การทำความเข้าใจข้อขัดข้องและบันทึกข้อขัดข้อง" 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
        }
    }
}

คุณอาจไม่จำเป็นต้องปิดกั้นการอ่านและทำให้คิวช้าลงโดยใช้การซิงค์ คุณสามารถใช้ซิงค์สำหรับการเขียนแบบอนุกรม
Basheer_CAD

6

ใช้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 คุณต้องแน่ใจว่าข้อความนั้นถูกส่งจากเธรดเดียวกันที่ส่งข้อความล็อกเริ่มต้น การปลดล็อคการล็อคจากเธรดอื่นอาจส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนด



6

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

2
นี่คือคำตอบที่ถูกต้องและไม่ใช่คำตอบที่ยอมรับและได้รับการสนับสนุนอย่างสูง (ขึ้นอยู่กับGCD) ดูเหมือนว่าไม่มีใครใช้หรือเข้าใจวิธีใช้เป็นThreadหลัก ฉันมีความสุขกับมัน - ในขณะที่GCDเต็มไปด้วย gotchas และข้อ จำกัด
javadba

4

ลอง: NSRecursiveLock

การล็อกที่อาจได้มาหลายครั้งโดยเธรดเดียวกันโดยไม่ทำให้เกิดการหยุดชะงัก

let lock = NSRecursiveLock()

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

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

2

รูปที่ฉันจะโพสต์การนำไปใช้งาน 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)งานได้ทั้งเป็นโมฆะและแบบอื่น ๆ นอกจากนี้ยังมีสิ่งปลูก
hnh

@hnh คุณหมายถึงอะไรโดย regrows? นอกจากนี้หากคุณยินดีที่จะแบ่งปันตัวอย่างการโทรไปที่วิธีการทั่วไปด้วยประเภท <T> ที่จะช่วยฉันอัปเดตคำตอบ - ฉันชอบที่คุณจะไปไหน
TheJeff

rethrows ไม่ใช่ regrows srz
hnh

1

รายละเอียด

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

1

ด้วยการห่อคุณสมบัติของ 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] = []

จากนั้นเข้าถึงตัวแปรตามปกติ


1
ฉันชอบวิธีนี้ แต่อยากรู้เกี่ยวกับค่าใช้จ่ายของคนที่ @Decorating ตั้งแต่ทำเช่นนั้นมีผลข้างเคียงของการสร้างDispatchQueueที่ถูกซ่อนไว้จากผู้ใช้ ฉันพบการอ้างอิงดังนั้นเพื่อทำให้ฉันสบายใจ: stackoverflow.com/a/35022486/1060314
Adam Venturella

เสื้อคลุมคุณสมบัติของตัวเองค่อนข้างเบา - เป็นโครงสร้างดังนั้นหนึ่งในสิ่งที่เบาที่สุดที่คุณสามารถทำได้ ขอบคุณสำหรับลิงค์ใน DispatchQueue ฉันอยู่ในใจของฉันที่จะทำการทดสอบประสิทธิภาพบางอย่างใน que.sync ห่อกับโซลูชั่นอื่น ๆ (และเมื่อเทียบกับไม่มีคิว) แต่ไม่ได้ทำ
drewster

1

โดยสรุปนี่คือวิธีทั่วไปที่รวมถึงมูลค่าส่งคืนหรือเป็นโมฆะและโยน

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


}

0

ทำไมจึงทำให้ล็อคและยุ่งยากยุ่งยาก? ใช้อุปสรรคการจัดส่ง

สิ่งกีดขวางการจัดส่งสร้างจุดประสานภายในคิวพร้อมกัน

ขณะที่กำลังทำงานไม่มีการอนุญาตให้บล็อกอื่นในคิวทำงานแม้ว่าจะพร้อมกันและมีแกนอื่น ๆ ก็ตาม

หากฟังดูเหมือนล็อคแบบเอกสิทธิ์ (เขียน) แสดงว่าเป็น บล็อกที่ไม่มีสิ่งกีดขวางสามารถถูกคิดว่าเป็นการล็อกแบบแบ่งใช้ (อ่าน)

ตราบใดที่การเข้าถึงทรัพยากรทั้งหมดถูกดำเนินการผ่านคิวอุปสรรคจะให้การซิงโครไนซ์ราคาถูกมาก


2
ฉันหมายถึงคุณกำลังสมมติว่าใช้คิว GCD เพื่อซิงโครไนซ์การเข้าถึง แต่ไม่ได้กล่าวถึงในคำถามเดิม และสิ่งกีดขวางจำเป็นเฉพาะกับคิวที่เกิดขึ้นพร้อมกัน - คุณสามารถใช้คิวอนุกรมเพื่อจัดคิวบล็อกที่ไม่รวมซึ่งกันและกันเพื่อเลียนแบบการล็อก
Bill

คำถามของฉันทำไมเลียนแบบล็อค? จากสิ่งที่ฉันอ่านล็อคจะหมดกำลังใจเนื่องจากค่าใช้จ่ายเทียบกับอุปสรรคภายในคิว
Frederick C. Lee

0

จาก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

0

dispatch_barrier_async เป็นวิธีที่ดีกว่าในขณะที่ไม่บล็อกเธรดปัจจุบัน

dispatch_barrier_async (accessQueue, {dictionary [object.ID] = object})


-5

อีกวิธีหนึ่งคือการสร้างซูเปอร์คลาสจากนั้นสืบทอด วิธีนี้คุณสามารถใช้ 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
        }
    }

10
-1 การสืบทอดช่วยให้คุณมีรูปแบบย่อยที่หลากหลายเพื่อเป็นการเพิ่มการแต่งงาน หลีกเลี่ยงในภายหลังหากคุณไม่ต้องการอดีต อย่าขี้เกียจ ชอบการแต่งเพื่อนำรหัสมาใช้ใหม่
Jano
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.