J'ai cherché dans le livre Swift, mais je ne trouve pas la version Swift de @synchronized. Comment puis-je faire de l'exclusion mutuelle dans Swift?
concurrency
mutex
swift
Facture
la source
la source
removeFirst()
?Réponses:
Vous pouvez utiliser GCD. Il est un peu plus verbeux que
@synchronized
, mais fonctionne comme un remplacement:la source
Je le cherchais moi-même et je suis arrivé à la conclusion qu'il n'y avait pas encore de construction native dans swift pour cela.
J'ai créé cette petite fonction d'aide basée sur une partie du code que j'ai vu de Matt Bridges et d'autres.
L'utilisation est assez simple
Il y a un problème que j'ai trouvé avec cela. Passer dans un tableau en tant qu'argument de verrouillage semble provoquer une erreur de compilation très obtuse à ce stade. Sinon, cela semble fonctionner comme souhaité.
la source
@synchronized
bloc, mais notez qu'il n'est pas identique à une véritable instruction de bloc intégrée comme le@synchronized
bloc dans Objective-C, car les instructionsreturn
etbreak
ne fonctionnent plus pour sortir de la fonction / boucle environnante comme ce serait le cas s'il s'agissait d'une déclaration ordinaire.defer
mot clé pour vous assurer d'objc_sync_exit
être appelé même en cas declosure
lancement.J'aime et utilise de nombreuses réponses ici, donc je choisirais celle qui vous convient le mieux. Cela dit, la méthode que je préfère lorsque j'ai besoin de quelque chose comme objective-c
@synchronized
utilise ladefer
déclaration introduite dans swift 2.La chose gentille au sujet de cette méthode, est que votre section critique peut sortir du bloc contenant une manière désirée quelconque (par exemple,
return
,break
,continue
,throw
), et « les déclarations dans l'instruction defer sont exécutées , peu importe la façon dont le contrôle du programme est transféré. » 1la source
lock
? Comment estlock
initialisé?lock
est tout objet objectif-c.Vous pouvez prendre en sandwich les déclarations entre
objc_sync_enter(obj: AnyObject?)
etobjc_sync_exit(obj: AnyObject?)
. Le mot clé @synchronized utilise ces méthodes sous les couvertures. c'est à direla source
objc_sync_enter
etobjc_sync_exit
les méthodes sont définies dans Objc-sync.h et sont open source: opensource.apple.com/source/objc4/objc4-371.2/runtime/…objc_sync_enter(…)
etobjc_sync_exit(…)
sont des en-têtes publics fournis par iOS / macOS / etc. API (on dirait qu'elles sont à l'intérieur….sdk
du chemin d'accèsusr/include/objc/objc-sync.h
) . La façon la plus simple de savoir si quelque chose est une API publique ou non est de (dans Xcode) taper le nom de la fonction (par exempleobjc_sync_enter()
; les arguments n'ont pas besoin d'être spécifiés pour les fonctions C) , puis essayez de cliquer avec la commande. S'il vous montre le fichier d'en-tête pour cette API, alors vous êtes bon (puisque vous ne pourriez pas voir l'en-tête s'il n'était pas public) .L'analogue de la
@synchronized
directive d'Objective-C peut avoir un type de retour arbitraire et unrethrows
comportement agréable dans Swift.L'utilisation de l'
defer
instruction permet de renvoyer directement une valeur sans introduire de variable temporaire.Dans Swift 2, ajoutez l'
@noescape
attribut à la fermeture pour permettre plus d'optimisations:Basé sur les réponses de GNewc [1] (où j'aime le type de retour arbitraire) et Tod Cunningham [2] (où j'aime
defer
).la source
SWIFT 4
Dans Swift 4, vous pouvez utiliser les files d'attente de répartition GCD pour verrouiller les ressources.
la source
.serial
semble indisponible. Mais.concurrent
est disponible. : /myObject.state = myObject.state + 1
simultanément, il ne comptera pas le nombre total d'opérations mais produira à la place une valeur non déterministe. Pour résoudre ce problème, le code appelant doit être encapsulé dans une file d'attente série afin que la lecture et l'écriture se produisent de manière atomique. Bien sûr, Obj-c@synchronised
a le même problème, donc dans ce sens, votre implémentation est correcte.myObject.state += 1
est une combinaison d'une opération de lecture puis d'écriture. Un autre thread peut encore entrer entre définir et écrire une valeur. Selon objc.io/blog/2018/12/18/atomic-variables , il serait plus facile d'exécuter leset
dans un bloc de synchronisation / fermeture à la place et non sous la variable elle-même.En utilisant la réponse de Bryan McLemore, je l'ai étendu pour prendre en charge les objets qui jettent dans un manoir sûr avec la capacité de report de Swift 2.0.
la source
rethrows
pour simplifier l'utilisation avec des fermetures sans lancement (pas besoin d'utilisertry
), comme indiqué dans ma réponse .Pour ajouter une fonction de retour, vous pouvez procéder comme suit:
Par la suite, vous pouvez l'appeler en utilisant:
la source
Swift 3
Ce code a la possibilité de rentrer et peut fonctionner avec les appels de fonction asynchrones. Dans ce code, après l'appel de someAsyncFunc (), une autre fermeture de fonction sur la file d'attente série sera traitée mais bloquée par semaphore.wait () jusqu'à ce que signal () soit appelé. internalQueue.sync ne doit pas être utilisé car il bloquera le thread principal si je ne me trompe pas.
objc_sync_enter / objc_sync_exit n'est pas une bonne idée sans gestion des erreurs.
la source
Dans la session "Understanding Crashes and Crash Logs" 414 de la WWDC 2018, ils montrent la manière suivante d'utiliser DispatchQueues avec synchronisation.
Dans swift 4 devrait être quelque chose comme ceci:
Quoi qu'il en soit, vous pouvez également effectuer des lectures plus rapidement en utilisant des files d'attente simultanées avec des barrières. Les lectures synchronisées et asynchrones sont effectuées simultanément et l'écriture d'une nouvelle valeur attend la fin des opérations précédentes.
la source
Utilisez NSLock dans Swift4:
la source
Dans le Swift 5 moderne, avec possibilité de retour:
Utilisez-le comme ceci, pour profiter de la capacité de valeur de retour:
Ou comme ça autrement:
la source
GCD
). Il semble que personne n'utilise ou ne comprenne comment l'utiliserThread
. J'en suis très satisfait - alors qu'ilGCD
est semé d'embûches et de limitations.Essayez: NSRecursiveLock
la source
Je vais publier mon implémentation de Swift 5, basée sur les réponses précédentes. Merci les gars! J'ai trouvé utile d'en avoir un qui renvoie également une valeur, j'ai donc deux méthodes.
Voici une classe simple à faire en premier:
Ensuite, utilisez-le comme si vous aviez besoin d'une valeur de retour:
Ou:
la source
public class func synced<T>(_ lock: Any, closure: () -> T)
, fonctionne pour les deux, nul et tout autre type. Il y a aussi les trucs de repousse.Détails
xCode 8.3.1, swift 3.1
Tâche
Lire la valeur d'écriture à partir de différents threads (async).
Code
Usage
Échantillon complet
la source
Avec les wrappers de propriétés de Swift, voici ce que j'utilise maintenant:
Ensuite, vous pouvez simplement faire:
ou
Accédez ensuite à la variable comme vous le feriez normalement.
la source
DispatchQueue
qui est caché à l'utilisateur. J'ai trouvé cette référence SO pour me mettre à l'aise: stackoverflow.com/a/35022486/1060314En conclusion, voici un moyen plus courant qui inclut la valeur de retour ou void, et lancez
la source
Pourquoi le rendre difficile et compliqué avec des serrures? Utilisez des barrières d'expédition.
Une barrière de répartition crée un point de synchronisation dans une file d'attente simultanée.
Pendant son exécution, aucun autre bloc de la file d'attente n'est autorisé à s'exécuter, même s'il est simultané et que d'autres cœurs sont disponibles.
Si cela ressemble à un verrou exclusif (écriture), ça l'est. Les blocs sans barrière peuvent être considérés comme des verrous partagés (en lecture).
Tant que tous les accès à la ressource sont effectués via la file d'attente, les barrières offrent une synchronisation très bon marché.
la source
Basé sur ɲeuroburɳ , testez un cas de sous-classe
Production:
la source
dispatch_barrier_async est le meilleur moyen, sans bloquer le thread actuel.
dispatch_barrier_async (accessQueue, {dictionary [object.ID] = object})
la source
Une autre méthode consiste à créer une superclasse puis à en hériter. De cette façon, vous pouvez utiliser GCD plus directement
la source