web-dev-qa-db-fra.com

Alternatives Mutex dans swift

J'ai une mémoire partagée entre plusieurs threads. Je veux empêcher ces threads d'accéder à ce morceau de mémoire en même temps. (comme problème producteur-consommateur )

Problème:

Un thread ajoute des éléments à une file d'attente et un autre thread lit ces éléments et les supprime. Ils ne doivent pas accéder à la file d'attente simultanément.

Une solution à ce problème consiste à utiliser Mutex.

Comme je l'ai trouvé, il n'y a pas de Mutex dans Swift. Existe-t-il des alternatives à Swift?

20
rick

Comme les gens l'ont commenté (y compris moi), il existe plusieurs façons de réaliser ce type de verrouillage. Mais je pense que l'envoi de sémaphore est meilleur que d'autres car il semble avoir le moins de frais généraux. Comme on le trouve dans Apples doc, "Replacing Semaphore Code" , il ne descend pas dans l'espace du noyau à moins que le sémaphore ne soit déjà verrouillé (= zéro), ce qui est le seul cas où le code descend dans le noyau pour changer de thread. Je pense que le sémaphore n'est pas nul la plupart du temps (ce qui est bien sûr une question spécifique à l'application, cependant). Ainsi, nous pouvons éviter beaucoup de frais généraux.

Un autre commentaire sur le sémaphore de répartition, qui est le scénario opposé à ci-dessus. Si vos threads ont des priorités d'exécution différentes et que les threads de priorité supérieure doivent verrouiller le sémaphore pendant une longue période, la répartition du sémaphore n'est peut-être pas la solution. C'est parce qu'il n'y a pas de "file d'attente" parmi les threads en attente. Ce qui se produit dans ce cas, c'est que les threads de priorité supérieure obtiennent et verrouillent le sémaphore la plupart du temps, et les threads de priorité inférieure ne peuvent verrouiller le sémaphore qu'occasionnellement, donc, la plupart du temps simplement en attente. Si ce comportement ne convient pas à votre application, vous devez plutôt envisager la file d'attente de répartition.

3
beshio

Il existe de nombreuses solutions pour cela, mais j'utilise des files d'attente série pour ce type d'action:

let serialQueue = DispatchQueue(label: "queuename")
serialQueue.sync { 
    //call some code here, I pass here a closure from a method
}

Edit/Update: Aussi pour les sémaphores:

let higherPriority = DispatchQueue.global(qos: .userInitiated)
let lowerPriority = DispatchQueue.global(qos: .utility)

let semaphore = DispatchSemaphore(value: 1)

func letUsPrint(queue: DispatchQueue, symbol: String) {
    queue.async {
        debugPrint("\(symbol) -- waiting")
        semaphore.wait()  // requesting the resource

        for i in 0...10 {
            print(symbol, i)
        }

        debugPrint("\(symbol) -- signal")
        semaphore.signal() // releasing the resource
    }
}

letUsPrint(queue: lowerPriority, symbol: "Low Priority Queue Work")
letUsPrint(queue: higherPriority, symbol: "High Priority Queue Work")

RunLoop.main.run()
8
Devanshu Saini

Grâce au commentaire de beshio , vous pouvez utiliser un sémaphore comme ceci:

let semaphore = DispatchSemaphore(value: 1)

utilisez attendre avant d'utiliser la ressource:

semaphore.wait()
// use the resource

et après avoir utilisé le relâcher:

semaphore.signal()

Faites cela dans chaque fil.

6
A.bee