106 votes

Les variables Swift sont-elles atomiques ?

En Objective-C, on distingue les propriétés atomiques et non atomiques :

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

D'après ce que j'ai compris, vous pouvez lire et écrire des propriétés définies comme atomiques à partir de plusieurs threads en toute sécurité, tandis que l'écriture et l'accès à des propriétés non atomiques ou à des ivars à partir de plusieurs threads en même temps peuvent entraîner un comportement non défini, y compris des erreurs de mauvais accès.

Donc si vous avez une variable comme ceci dans Swift :

var object: NSObject

Puis-je lire et écrire dans cette variable en parallèle en toute sécurité ? (Sans tenir compte de la signification réelle de cette opération).

0 votes

Je pense qu'à l'avenir, nous pourrons peut-être utiliser @atomic o @nonatomic . ou simplement atomique par défaut. (Swift est tellement incomplet qu'on ne peut pas dire grand chose pour l'instant)

1 votes

IMO, ils rendront tout non-atomique par défaut, et fourniront probablement une fonction spéciale pour rendre les choses atomiques.

0 votes

En passant, atomic n'est généralement pas considéré comme suffisant pour une interaction sûre avec une propriété, sauf pour les types de données simples. Pour les objets, on synchronise généralement l'accès entre les threads en utilisant des verrous (par ex, NSLock o @synchronized ) ou des files d'attente GCD (par exemple, une file d'attente série ou une file d'attente concurrente avec un modèle "lecteur-écrivain").

3voto

jamone Points 6458

Voici le wrapper de propriété atomique que j'utilise intensivement. J'ai fait du mécanisme de verrouillage un protocole, afin de pouvoir expérimenter différents mécanismes. J'ai essayé les sémaphores, DispatchQueues et le pthread_rwlock_t . Le site pthread_rwlock_t a été choisi parce qu'il semble avoir la plus faible surcharge, et une plus faible chance d'une inversion de priorité.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X