ऑब्जेक्टिव-सी के "@synchronized" के बराबर स्विफ्ट क्या है?


231

मैंने स्विफ्ट पुस्तक खोज ली है, लेकिन @synchronized के स्विफ्ट संस्करण को नहीं ढूँढ सकता। मैं स्विफ्ट में आपसी बहिष्कार कैसे करूं?


1
मैं एक प्रेषण अवरोध का उपयोग करूंगा। बाधाएं बहुत सस्ते सिंक्रनाइज़ेशन प्रदान करती हैं। dispatch_barrier_async ()। आदि
फ्रेडरिक सी। ली।

@ FrederickC.Lee, क्या होगा अगर आपको लिखने के लिए एक सिंक्रनाइज़ेशन की आवश्यकता है , जैसे कि एक आवरण बनाते समय removeFirst()?
स्कूटीब्लैड्स

जवाबों:


183

आप जीसीडी का उपयोग कर सकते हैं। यह तुलना में थोड़ा अधिक है @synchronized, लेकिन प्रतिस्थापन के रूप में काम करता है:

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

12
यह बहुत अच्छा है, लेकिन आपके पास @synchronized के साथ री-एंट्री की क्षमता का अभाव है।
माइकल झरना

9
इस दृष्टिकोण के साथ आपको सावधान रहने की आवश्यकता है। आपके ब्लॉक को किसी अन्य थ्रेड पर निष्पादित किया जा सकता है। एपीआई डॉक्स कहते हैं: "अनुकूलन के रूप में, यह फ़ंक्शन वर्तमान थ्रेड पर ब्लॉक को संभव होने पर आमंत्रित करता है।"
जैव

20
इस बारे में मैट गैलाघर का महान लेख: cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html
wuf810

4
नहींं, यह कभी-कभी गतिरोध का कारण बनता है।
टॉम क्रेना

70
नहीं, नहीं और नहीं। अच्छी कोशिश, लेकिन अच्छी तरह से काम करता है। क्यों? आवश्यक पठन (विकल्प, सावधानियों की व्यापक तुलना) और मैट गैलाघर से एक महान उपयोगिता ढांचा, यहाँ: cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html @ wuf810 ने इस पहले (HT) का उल्लेख किया है, लेकिन समझ में आया कि यह लेख कितना अच्छा है। सभी को पढ़ना चाहिए। (कृपया इसे शुरू में दिखाई देने के लिए न्यूनतम से
बढ़ाएं

181

मैं खुद इस की तलाश कर रहा था और इस निष्कर्ष पर आया कि इसके लिए अभी तक स्विफ्ट के अंदर कोई देशी निर्माण नहीं हुआ है।

मैंने मैट ब्रिज और दूसरों से देखे गए कुछ कोड के आधार पर इस छोटे सहायक समारोह का निर्माण किया।

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ब्लॉक के सिंटैक्स को अच्छी तरह से संरक्षित करता है , लेकिन ध्यान दें कि यह एक वास्तविक बिलिन ब्लॉक स्टेटमेंट के समान नहीं है @synchronizedजो ऑब्जेक्टिव-सी में ब्लॉक की तरह है, क्योंकि returnऔर breakस्टेटमेंट अब आसपास के फ़ंक्शन / लूप से बाहर निकलने के लिए काम नहीं करते हैं यदि यह एक सामान्य कथन होता।
नवांश

3
त्रुटि की संभावना है कि सरणियों को मानों के रूप में पारित किया जाता है न कि संदर्भ
james_alvarez

9
यह संभवत: नए deferकीवर्ड का उपयोग करने के लिए एक महान स्थान होगा ताकि यह सुनिश्चित किया जा सके objc_sync_exitकि क्या closureफेंकता है।
devios1

3
@ t0rst लिंक्ड-इन लेख के आधार पर इस जवाब को "त्रुटिपूर्ण" कहना मान्य नहीं है। लेख कहता है कि यह विधि "आदर्श की तुलना में थोड़ी धीमी है" और "एप्पल प्लेटफार्मों तक सीमित है"। यह लंबे शॉट द्वारा इसे "त्रुटिपूर्ण" नहीं बनाता है।
RenniePet

150

मुझे यहां बहुत से उत्तर पसंद हैं और इसलिए मैं आपके लिए सबसे अच्छा काम करता हूं। उस ने कहा, जिस विधि को मैं पसंद करता हूं जब मुझे ऑब्जेक्टिव-सी की आवश्यकता होती है तो स्विफ्ट 2 में पेश किए @synchronizedगए deferकथन का उपयोग करता है ।

{ 
    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), और "आस्थगित करें कथन में बयान चाहे कितना कार्यक्रम नियंत्रण स्थानांतरित कर रहा है क्रियान्वित कर रहे हैं।" 1


मुझे लगता है कि यह संभवतः यहां प्रदान किया गया सबसे सुंदर समाधान है। आपकी प्रतिक्रिया के लिए धन्यवाद।
स्कॉट डी

3
क्या है lock? कैसे lockशुरू किया जाता है?
वान ड्यू ट्रान

6
lockकिसी भी उद्देश्य-सी वस्तु है।
17euroburɳ 17

1
अति उत्कृष्ट! जब स्विफ्ट 1 को पेश किया गया था और मैंने कुछ समय में इन पर दोबारा गौर नहीं किया था, तो मैंने कुछ लॉक हेल्पर तरीके लिखे थे। पूरी तरह से defer के बारे में भूल गया; जाने का यह रास्ता है!
रैंडी

मुझे यह पसंद है लेकिन एक कंपाइलर त्रुटि प्राप्त करें "स्टेटमेंट्स के ब्रेस्ड ब्लॉक एक अप्रयुक्त बंद" है। एक्सकोड 8. आह मुझे लगता है कि वे सिर्फ फ़ंक्शन ब्रेस हैं - आपके "1" संदर्भ लिंक को खोजने के लिए थोड़ी देर - धन्यवाद!
डंकन ग्रोनेवल्ड

83

तुम objc_sync_enter(obj: AnyObject?)और के बीच बयान सैंडविच कर सकते हैं objc_sync_exit(obj: AnyObject?)। @Synchronized कीवर्ड कवर के तहत उन तरीकों का उपयोग कर रहा है। अर्थात

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

3
क्या इसे Apple द्वारा निजी API का उपयोग माना जाएगा?
Drux

2
नहीं है, objc_sync_enterऔर objc_sync_exitObjC-sync.h में परिभाषित तरीके हैं और खुला स्रोत हैं: opensource.apple.com/source/objc4/objc4-371.2/runtime/...
bontoJR

यदि एकाधिक थ्रेड्स एक ही संसाधन तक पहुंचने का प्रयास करते हैं, तो क्या दूसरा एक प्रतीक्षा, पुन: प्रयास या दुर्घटना करता है?
TruMan1

@BontoJR ने जो कहा, उस पर जोड़ते हुए, objc_sync_enter(…)और objc_sync_exit(…)iOS / macOS / etc द्वारा प्रदान किए गए सार्वजनिक हेडर हैं। एपीआई (ऐसा लगता है कि वे ….sdkरास्ते में अंदर हैं usr/include/objc/objc-sync.h) । यह पता लगाने का सबसे आसान तरीका है कि कुछ सार्वजनिक एपीआई है या नहीं (एक्सकोड में) फ़ंक्शन का नाम टाइप करें (जैसे objc_sync_enter(), तर्क को सी फ़ंक्शन के लिए निर्दिष्ट करने की आवश्यकता नहीं है) , फिर इसे कमांड-क्लिक करने का प्रयास करें। यदि यह आपको उस एपीआई के लिए हेडर फाइल दिखाता है, तो आप अच्छे हैं (क्योंकि आप हेडर को नहीं देख पाएंगे अगर वह सार्वजनिक नहीं था)
स्लिप डी। थॉम्पसन

75

@synchronizedऑब्जेक्टिव-सी से निर्देशन का एनालॉग rethrowsस्विफ्ट में एक मनमाना रिटर्न प्रकार और अच्छा व्यवहार हो सकता है।

// 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स्टेटमेंट का उपयोग अस्थायी वैरिएबल को पेश किए बिना सीधे मान वापस करने की सुविधा देता है।


स्विफ्ट 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)।


Xcode मुझे बता रहा है कि @noescape अब डिफ़ॉल्ट है और स्विफ्ट 3 में
अपदस्थ है

यह सही है, इस उत्तर में कोड स्विफ्ट 2 के लिए है और स्विफ्ट 3 के लिए कुछ अनुकूलन की आवश्यकता है। मेरे पास समय होने पर मैं इसे अपडेट करूंगा।
werediver

1
क्या आप उपयोग की व्याख्या कर सकते हैं? शायद एक उदाहरण के साथ .. अग्रिम धन्यवाद! मेरे मामले में, मेरे पास एक सेट है जिसे मुझे सिंक्रनाइज़ करने की आवश्यकता है, क्योंकि मैं एक डिस्पैचक्यू में इसकी सामग्री में हेरफेर करता हूं।
सांचो

@ मैं इस पोस्ट को संक्षिप्त रखना चाहूंगा। आप सामान्य समवर्ती प्रोग्रामिंग दिशानिर्देशों के बारे में पूछना चाहते हैं, यह एक व्यापक प्रश्न है। इसे एक अलग प्रश्न के रूप में पूछने की कोशिश करें!
वेदराइवर

41

स्विफ्ट 4

स्विफ्ट 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उपलब्ध है। : /
ट्रैविस ग्रिग्स

2
डिफ़ॉल्ट है .serial
डंकन

2
ध्यान दें कि यह पैटर्न सबसे आम मल्टी थ्रेड मुद्दों के खिलाफ ठीक से गार्ड नहीं करता है। उदाहरण के लिए, यदि आप myObject.state = myObject.state + 1समवर्ती रूप से भाग लेंगे, तो यह कुल संचालन की गणना नहीं करेगा, बल्कि इसके बजाय एक nondeterministic मान प्राप्त करेगा। उस समस्या को हल करने के लिए, कॉलिंग कोड को एक सीरियल कतार में लपेटा जाना चाहिए ताकि रीड और राइट दोनों परमाणु रूप से हो सकें। बेशक ओब्ज-सी की @synchronisedएक ही समस्या है, इसलिए इस अर्थ में आपका कार्यान्वयन सही है।
बेरिक

1
हां, myObject.state += 1रीड का संयोजन है और फिर राइट ऑपरेशन है। कुछ अन्य थ्रेड अभी भी मान सेट करने / लिखने के लिए इनबेटीइन आ सकते हैं। Objc.io/blog/2018/12/18/atomic-variables के अनुसार , setसिंक ब्लॉक / क्लोजर को चलाने के बजाय इसे चलाने के लिए आसान होगा और चर के तहत नहीं।
साइबरमेव १ew

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

ब्रायन मैकलमोर उत्तर का उपयोग करते हुए, मैंने इसे उन वस्तुओं का समर्थन करने के लिए बढ़ाया जो स्विफ्ट 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

इस कोड में फिर से प्रवेश की क्षमता है और यह एसिंक्रोनस फ़ंक्शन कॉल के साथ काम कर सकता है। इस कोड में, someAsyncFunc () को कॉल करने के बाद, सीरियल कतार पर एक और फंक्शन क्लोजर प्रोसेस होगा लेकिन semaphore.wait () द्वारा तब तक ब्लॉक किया जाएगा जब तक सिग्नल () नहीं कहा जाता है। अगर मैं गलत नहीं हूँ तो यह मुख्य धागे को अवरुद्ध कर देगा क्योंकि इंटरन्यू क्यू.सुंक का उपयोग नहीं किया जाना चाहिए।

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 / बाहर निकलने का उपयोग नहीं करके, आप कुछ पर्याप्त प्रदर्शन लाभ देते हैं।
gnasher729 20

8

2018 के WWDC के "अंडरस्टैंडिंग क्रैश और क्रैश लॉग" सत्र 414 में वे सिंक के साथ डिस्पैचक्यू का उपयोग करके निम्न तरीके दिखाते हैं।

स्विफ्ट 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
        }
    }
}

आपको शायद रीड्स को ब्लॉक करने और सिंक का उपयोग करके कतार को धीमा करने की आवश्यकता नहीं है। आप बस धारावाहिक लेखन के लिए सिंक का उपयोग कर सकते हैं।
बशीर_काड

6

Swift4 में NSLock का उपयोग करें :

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

आधुनिक स्विफ्ट 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गोटे और मर्यादाओं से भरा हुआ है।
javadba

4

कोशिश करें: NSRecursiveLock

एक लॉक जिसे एक ही थ्रेड द्वारा एक गतिरोध पैदा किए बिना कई बार प्राप्त किया जा सकता है।

let lock = NSRecursiveLock()

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

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

2

चित्र मैं अपने स्विफ्ट 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), दोनों, शून्य और किसी अन्य प्रकार के लिए काम करता है। रेगो सामान भी है।
16

@ क्या आप 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
}

पूरा नमूना

विस्तार डिस्पैचग्रुप

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

स्विफ्ट की संपत्ति के रैपर के साथ, यह वह है जो मैं अभी उपयोग कर रहा हूं:

@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
एडम वेंचरला

संपत्ति का आवरण अपने आप में काफी हल्का है - बस एक संरचना है, इसलिए, सबसे हल्की चीजों में से एक जिसे आप बना सकते हैं। हालांकि डिस्पैचक्व्यू पर लिंक के लिए धन्यवाद। मैं अपने मन के पीछे कतार में कुछ प्रदर्शन परीक्षण करने के लिए किया है। अन्य समाधान बनाम (या कोई कतार नहीं) लपेटें, लेकिन ऐसा नहीं किया था।
ड्रूस्टर

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 कतार का उपयोग कर रहे हैं, लेकिन मूल प्रश्न में इसका उल्लेख नहीं है। और केवल एक समवर्ती कतार के साथ एक अवरोध आवश्यक है - आप केवल लॉक का अनुकरण करने के लिए पारस्परिक रूप से बहिष्कृत ब्लॉकों को कतारबद्ध करने के लिए एक धारावाहिक कतार का उपयोग कर सकते हैं।
बिल

मेरा सवाल है, एक ताला क्यों अनुकरण? मैंने जो पढ़ा है, उसमें ओवरहेड बनाम एक कतार के भीतर एक बाधा के कारण ताले हतोत्साहित किए गए हैं।
फ्रेडरिक सी। ली।

0

Oneuroburɳ के आधार पर , एक उप-श्रेणी के मामले का परीक्षण करें

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 [ऑब्जेक्ट.आईडी] = ऑब्जेक्ट})


-5

एक और तरीका है एक सुपरक्लास बनाना और फिर उसे विरासत में देना। इस तरह आप जीसीडी का अधिक सीधे उपयोग कर सकते हैं

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

9
-1 इनहेरिटेंस आपको युग्मन बढ़ाने के बदले में उप-प्रकार बहुरूपता देता है। यदि आपको पूर्व की आवश्यकता नहीं है, तो बाद में बचें। आलसी मत बनो। कोड पुन: उपयोग के लिए संरचना को प्राथमिकता दें।
जेनो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.