সুইফট ভেরিয়েবল কি পারমাণবিক?


102

অবজেক্টিভ-সিতে আপনার পারমাণবিক এবং ননোটমিক বৈশিষ্ট্যের মধ্যে পার্থক্য রয়েছে:

@property (nonatomic, strong) NSObject *nonatomicObject;
@property (atomic, strong) NSObject *atomicObject;

আমার বোধগম্যতা থেকে আপনি একাধিক থ্রেড থেকে অ্যাটমিক হিসাবে নিরাপদে সংজ্ঞায়িত বৈশিষ্ট্যগুলি নিরাপদে পড়তে বা লিখতে পারেন, একই সময়ে একাধিক থ্রেড থেকে ননোটমিক বৈশিষ্ট্য বা আইভারগুলি অ্যাক্সেস করার সময় খারাপ অ্যাক্সেস ত্রুটি সহ অনির্ধারিত আচরণের ফলাফল হতে পারে।

সুতরাং আপনার যদি সুইফটে এর মত চলক থাকে:

var object: NSObject

আমি কি নিরাপদে নিরাপদে সমান্তরালে এই পরিবর্তনশীলটি লিখতে এবং লিখতে পারি? (এটি করার আসল অর্থ বিবেচনা না করে)।


আমি ভবিষ্যতে ভাবি, সম্ভবত আমরা ব্যবহার করতে পারি @atomicবা @nonatomic। বা ডিফল্টরূপে কেবল পারমাণবিক। (সুইফট এত অসম্পূর্ণ, আমরা এখন অনেক কিছুই বলতে পারি না)
ব্রায়ান চেন

1
আইএমও, তারা ডিফল্টরূপে সমস্ত কিছু অ-পরমাণু করে দেবে এবং পারমাণবিক স্টাফ তৈরির জন্য সম্ভবত একটি বিশেষ বৈশিষ্ট্য সরবরাহ করবে।
eonil

একটি সরাইয়া হিসাবে, atomicসাধারণত একটি সম্পত্তি সহ থ্রেড-নিরাপদ মিথষ্ক্রিয়া জন্য যথেষ্ট বিবেচনা করা হয় না, সহজ ধরনের তথ্য ছাড়া। অবজেক্টের জন্য, কেউ সাধারণত লকগুলি (যেমন, NSLockবা @synchronized) বা জিসিডি সারিগুলি (যেমন, সিরিয়াল সারি বা "পাঠক-লেখক" প্যাটার্ন সহ সমবর্তী সারি) ব্যবহার করে থ্রেডগুলিতে অ্যাক্সেসকে সিঙ্ক্রোনাইজ করে ।
রব

@ রব, সত্য, যদিও অবজেক্টিভ সি-তে (এবং সম্ভবত সুইফটে) রেফারেন্স গণনার কারণে পারমাণবিক অ্যাক্সেস ছাড়াই একযোগে পড়া এবং লেখার ফলে স্মৃতি দুর্নীতি হতে পারে। যদি সমস্ত ভেরিয়েবলের মধ্যে পারমাণবিক অ্যাক্সেস থাকে তবে এটি ঘটতে পারে এমন একটি "যৌক্তিক" জাতি শর্ত, অর্থাৎ অপ্রত্যাশিত আচরণ।
লাসসেজ

আমাকে ভুল করবেন না: আমি আশা করি অ্যাপল পারমাণবিক আচরণের প্রশ্নের উত্তর / সমাধান করে। এটি ঠিক যে (ক) atomicবস্তুর থ্রেড-সুরক্ষা নিশ্চিত করে না; এবং (খ) থ্রেড-সুরক্ষা (অন্য বিষয়গুলির মধ্যে একই সাথে পড়া / লেখাগুলি প্রতিরোধ করা) নিশ্চিত করার জন্য যদি কেউ পূর্বোক্ত সিঙ্ক্রোনাইজেশন কৌশলগুলির সঠিকভাবে ব্যবহার করে তবে পারমাণবিক সমস্যাটি মোটা। তবে আমাদের এখনও সাধারণ ডেটা ধরণের জন্য এটি প্রয়োজন / চাই, যেখানে atomicসত্যিকারের মান আছে। ভাল প্রশ্ন!
রব

উত্তর:


52

নিম্ন স্তরের কোনও ডকুমেন্টেশন উপলব্ধ না হওয়ায় এটি খুব তাড়াতাড়ি অনুমান করা যায় তবে আপনি সমাবেশ থেকে অধ্যয়ন করতে পারেন। হপার ডিসাসেমব্লার একটি দুর্দান্ত সরঞ্জাম।

@interface ObjectiveCar : NSObject
@property (nonatomic, strong) id engine;
@property (atomic, strong) id driver;
@end

ব্যবহার করে objc_storeStrongএবং objc_setProperty_atomicযথাক্রমে ননোটমিক এবং পারমাণবিক জন্য where

class SwiftCar {
    var engine : AnyObject?    
    init() {
    }
}

স্পষ্টতই, swift_retainথেকে ব্যবহার করে libswift_stdlib_coreএবং থ্রেড সুরক্ষাটি অন্তর্নির্মিত নয়।

আমরা অনুমান করতে পারি যে অতিরিক্ত কীওয়ার্ডগুলি (অনুরূপ @lazy) পরে প্রবর্তিত হতে পারে।

07/20/15 আপডেট করুন : সিলেটলেটের এই ব্লগপোস্ট অনুসারে দ্রুত পরিবেশটি আপনার জন্য কিছু ক্ষেত্রে থ্রেডকে নিরাপদ করে তুলতে পারে, যেমন:

class Car {
    static let sharedCar: Car = Car() // will be called inside of dispatch_once
}

private let sharedCar: Car2 = Car2() // same here
class Car2 {

}

05/25/16 আপডেট করুন : দ্রুত বিবর্তন প্রস্তাবের জন্য নজর রাখুন https://github.com/apple/swift-evolution/blob/master/proposals/0030-property-behavier-decls.md - দেখে মনে হচ্ছে এটি @atomicনিজের দ্বারা আচরণ বাস্তবায়িত করা সম্ভব হবে ।


আমি কিছু সাম্প্রতিক তথ্যের সাথে আমার উত্তর আপডেট করেছি, আশা করি এটি সাহায্য করবে
সাশ জাটস

1
আরে, হপার ডিসসেস্মবলার সরঞ্জামটির লিঙ্কটির জন্য ধন্যবাদ। ঝরঝরে লাগছে।
C0D3

11

থ্রেড সুরক্ষার আশেপাশে সুইফ্টের কোনও ভাষা গঠন নেই। ধারণা করা হচ্ছে আপনি প্রদত্ত গ্রন্থাগারগুলি নিজের থ্রেড সুরক্ষা ব্যবস্থাপনার জন্য ব্যবহার করবেন। থ্রেড সুরক্ষা বাস্তবায়নে আপনার কাছে প্রচুর বিকল্প রয়েছে যা মাইটেক্স মেকানিজম হিসাবে পাইথ্রেড মিউটেক্সেস, এনএসলক, এবং ডিসপ্যাচ_সেন্সি সহ thread বিষয়টিতে মাইক অ্যাশের সাম্প্রতিক পোস্টটি দেখুন: https://mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html সুতরাং আপনার "প্রশ্নের উত্তরটির সরাসরি উত্তর আমি সমান্তরালভাবে নিরাপদে এই পরিবর্তনশীলটি পড়ি এবং লিখি? " কোন.


7

সম্ভবত এই প্রশ্নের উত্তর দেওয়া খুব তাড়াতাড়ি। বর্তমানে সুইফ্টের অ্যাক্সেস মডিফায়ারের অভাব রয়েছে, সুতরাং কোড যুক্ত করার কোনও সুস্পষ্ট উপায় নেই যা কোনও প্রপার্টি গেটার / সেটারের আশেপাশে সামঞ্জস্য পরিচালনা করে। তদ্ব্যতীত, সুইফট ল্যাঙ্গুয়েজে এখনও সম্মতি সম্পর্কিত কোনও তথ্য আছে বলে মনে হয় না! (এটিতে কেভিও ইত্যাদির অভাব রয়েছে ...)

আমি মনে করি ভবিষ্যতে প্রকাশে এই প্রশ্নের উত্তর পরিষ্কার হয়ে যাবে।


Re: KVO অভাবে খুঁজে বার করো willSet, didSet- পথে প্রথম পদক্ষেপ হবে বলে মনে হচ্ছে
Sash Zats

1
উইলসেট, ডিডসেট এমন বৈশিষ্ট্যগুলির জন্য আরও বেশি যা সর্বদা একটি কাস্টম সেটার প্রয়োজন কারণ তাদের কিছু করতে হয়েছিল। উদাহরণস্বরূপ, কোনও রঙের সম্পত্তি যা যখন সম্পত্তিটি অন্য কোনও মানতে পরিবর্তিত হয় তখন একটি ভিউ পুনরায় চিত্রিত করা প্রয়োজন; ডিডসেট ব্যবহার করে এটি এখন সহজ কাজ।
gnasher729

হ্যাঁ, এটিই আমি "প্রথম পদক্ষেপ" বলতে
চাইছিলাম

6

বিস্তারিত

  • এক্সকোড 9.1, সুইফট 4
  • এক্সকোড 10.2.1 (10E1001), সুইফট 5

লিংক

প্রয়োগিত প্রকারের

মূল ধারণা

class Example {

    private lazy var semaphore = DispatchSemaphore(value: 1)

    func executeThreadSafeFunc1() {
        // Lock access. Only first thread can execute code below.
        // Other threads will wait until semaphore.signal() will execute
        semaphore.wait()
        // your code
        semaphore.signal()         // Unlock access
    }

    func executeThreadSafeFunc2() {
        // Lock access. Only first thread can execute code below.
        // Other threads will wait until semaphore.signal() will execute
        semaphore.wait()
        DispatchQueue.global(qos: .background).async {
            // your code
            self.semaphore.signal()         // Unlock access
        }
    }
}

পারমাণবিক অ্যাক্সেসের নমুনা

class Atomic {

    let dispatchGroup = DispatchGroup()
    private var variable = 0

    // Usage of semaphores

    func semaphoreSample() {

        // value: 1 - number of threads that have simultaneous access to the variable
        let atomicSemaphore = DispatchSemaphore(value: 1)
        variable = 0

        runInSeveralQueues { dispatchQueue  in
            // Only (value) queqes can run operations betwen atomicSemaphore.wait() and atomicSemaphore.signal()
            // Others queues await their turn
            atomicSemaphore.wait()            // Lock access until atomicSemaphore.signal()
            self.variable += 1
            print("\(dispatchQueue), value: \(self.variable)")
            atomicSemaphore.signal()          // Unlock access
        }

        notifyWhenDone {
            atomicSemaphore.wait()           // Lock access until atomicSemaphore.signal()
            print("variable = \(self.variable)")
            atomicSemaphore.signal()         // Unlock access
        }
    }

    // Usage of sync of DispatchQueue

    func dispatchQueueSync() {
        let atomicQueue = DispatchQueue(label: "dispatchQueueSync")
        variable = 0

        runInSeveralQueues { dispatchQueue  in

            // Only queqe can run this closure (atomicQueue.sync {...})
            // Others queues await their turn
            atomicQueue.sync {
                self.variable += 1
                print("\(dispatchQueue), value: \(self.variable)")
            }
        }

        notifyWhenDone {
            atomicQueue.sync {
                print("variable = \(self.variable)")
            }
        }
    }

    // Usage of objc_sync_enter/objc_sync_exit

    func objcSync() {
        variable = 0

        runInSeveralQueues { dispatchQueue  in

            // Only one queqe can run operations betwen objc_sync_enter(self) and objc_sync_exit(self)
            // Others queues await their turn
            objc_sync_enter(self)                   // Lock access until objc_sync_exit(self).
            self.variable += 1
            print("\(dispatchQueue), value: \(self.variable)")
            objc_sync_exit(self)                    // Unlock access
        }

        notifyWhenDone {
            objc_sync_enter(self)                   // Lock access until objc_sync_exit(self)
            print("variable = \(self.variable)")
            objc_sync_exit(self)                    // Unlock access
        }
    }
}

// Helpers

extension Atomic {

    fileprivate func notifyWhenDone(closure: @escaping ()->()) {
        dispatchGroup.notify(queue: .global(qos: .utility)) {
            closure()
            print("All work done")
        }
    }

    fileprivate func runInSeveralQueues(closure: @escaping (DispatchQueue)->()) {

        async(dispatch: .main, closure: closure)
        async(dispatch: .global(qos: .userInitiated), closure: closure)
        async(dispatch: .global(qos: .utility), closure: closure)
        async(dispatch: .global(qos: .default), closure: closure)
        async(dispatch: .global(qos: .userInteractive), closure: closure)
    }

    private func async(dispatch: DispatchQueue, closure: @escaping (DispatchQueue)->()) {

        for _ in 0 ..< 100 {
            dispatchGroup.enter()
            dispatch.async {
                let usec = Int(arc4random()) % 100_000
                usleep(useconds_t(usec))
                closure(dispatch)
                self.dispatchGroup.leave()
            }
        }
    }
}

ব্যবহার

Atomic().semaphoreSample()
//Atomic().dispatchQueueSync()
//Atomic().objcSync()

ফলাফল

এখানে চিত্র বর্ণনা লিখুন


গিথুবের একটি নমুনা প্রকল্পটি দুর্দান্ত হবে!
ক্লাস

1
হ্যালো! এটি সম্পূর্ণ নমুনা। Atomicক্লাসটি অনুলিপি করুন এবং এটি ব্যবহার করে চালানAtomic().semaphoreSample()
ভ্যাসিলি বোদনারুকুক

হ্যাঁ, আমি ইতিমধ্যে করেছি। ভেবেছিলাম এটি দুর্দান্ত হবে, এমন একটি প্রকল্প হিসাবে এটি সর্বাধিক বর্তমান সিনট্যাক্সে আপডেট হয়েছে। সুইফ্টের সাথে সিনট্যাক্স সব সময় পরিবর্তন হয়। এবং আপনার উত্তরটি একেবারে সাম্প্রতিক এক :)
ক্লাস

1

সুইফ্ট 5.1 থেকে আপনি আপনার সম্পত্তিগুলির জন্য নির্দিষ্ট যুক্তি তৈরি করতে সম্পত্তি র‌্যাপার ব্যবহার করতে পারেন । এটি পারমাণবিক মোড়কের বাস্তবায়ন:

@propertyWrapper
struct atomic<T> {
    private var value: T
    private let lock = NSLock()

    init(wrappedValue value: T) {
        self.value = value
    }

    var wrappedValue: T {
      get { getValue() }
      set { setValue(newValue: newValue) }
    }

    func getValue() -> T {
        lock.lock()
        defer { lock.unlock() }

        return value
    }

    mutating func setValue(newValue: T) {
        lock.lock()
        defer { lock.unlock() }

        value = newValue
    }
}

ব্যবহারবিধি:

class Shared {
    @atomic var value: Int
...
}

0

এখানে আমি পারমাণবিক সম্পত্তির মোড়ক ব্যবহার করি। আমি প্রকৃত লকিং মেকানিজমকে একটি প্রোটোকল বানিয়েছি, তাই আমি বিভিন্ন প্রক্রিয়া নিয়ে পরীক্ষা-নিরীক্ষা করতে পারি। আমি semaphores চেষ্টা DispatchQueues, এবং pthread_rwlock_tpthread_rwlock_tকারণ এটি সর্বনিম্ন ওভারহেড, এবং একটি অগ্রাধিকার বিলোম একটি নিম্ন সুযোগ আছে মনে হচ্ছে, নির্বাচন করা হয়েছিল।

/// Defines a basic signature that all locks will conform to. Provides the basis for atomic access to stuff.
protocol Lock {
    init()
    /// Lock a resource for writing. So only one thing can write, and nothing else can read or write.
    func writeLock()
    /// Lock a resource for reading. Other things can also lock for reading at the same time, but nothing else can write at that time.
    func readLock()
    /// Unlock a resource
    func unlock()
}

final class PThreadRWLock: Lock {
    private var rwLock = pthread_rwlock_t()

    init() {
        guard pthread_rwlock_init(&rwLock, nil) == 0 else {
            preconditionFailure("Unable to initialize the lock")
        }
    }

    deinit {
        pthread_rwlock_destroy(&rwLock)
    }

    func writeLock() {
        pthread_rwlock_wrlock(&rwLock)
    }

    func readLock() {
        pthread_rwlock_rdlock(&rwLock)
    }

    func unlock() {
        pthread_rwlock_unlock(&rwLock)
    }
}

/// A property wrapper that ensures atomic access to a value. IE only one thing can write at a time.
/// Multiple things can potentially read at the same time, just not during a write.
/// By using `pthread` to do the locking, this safer then using a `DispatchQueue/barrier` as there isn't a chance
/// of priority inversion.
@propertyWrapper
public final class Atomic<Value> {

    private var value: Value
    private let lock: Lock = PThreadRWLock()

    public init(wrappedValue value: Value) {
        self.value = value
    }

    public var wrappedValue: Value {
        get {
            self.lock.readLock()
            defer { self.lock.unlock() }
            return self.value
        }
        set {
            self.lock.writeLock()
            self.value = newValue
            self.lock.unlock()
        }
    }

    /// Provides a closure that will be called synchronously. This closure will be passed in the current value
    /// and it is free to modify it. Any modifications will be saved back to the original value.
    /// No other reads/writes will be allowed between when the closure is called and it returns.
    public func mutate(_ closure: (inout Value) -> Void) {
        self.lock.writeLock()
        closure(&value)
        self.lock.unlock()
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.