J'ai une application où j'ai une ressource partagée (un système Motion) à laquelle peuvent accéder plusieurs clients. J'ai des opérations individuelles qui nécessitent un accès au système pendant la durée du déplacement et qui devraient lever des exceptions "Occupé" si des opérations conflictuelles sont demandées en même temps. J'ai également des séquenceurs qui doivent acquérir un accès exclusif au système Motion pour l'exécution de plusieurs opérations, entrecoupées d'autres actions; pendant toute la séquence, aucun autre client ne doit pouvoir exécuter Operations.
J'ai traditionnellement abordé cela en utilisant l'affinité de thread, afin qu'un thread puisse demander un accès exclusif et exécuter des appels de blocage correspondant aux opérations. Bien que le thread ait accès, aucun autre thread ne peut utiliser la ressource. Le problème que j'ai maintenant est que je me suis déplacé vers l'implémentation de mon système en utilisant des modèles asynchrones/en attente, pour permettre une implémentation plus propre du séquenceur. Le problème est que maintenant mon séquenceur ne fonctionne pas toujours sur le même thread; le thread actif peut changer au cours des rappels, il n'est donc plus facile de déterminer si je suis dans un contexte valide pour continuer à exécuter les opérations. Un élément à noter est que certaines des opérations elles-mêmes sont composées d'attentes, ce qui signifie que les séquences et les opérations individuelles peuvent s'étendre sur plusieurs threads.
Ma question: quelqu'un connaît-il un bon schéma pour gérer l'acquisition exclusive en présence de commutation de thread en raison de l'async/wait?
Pour référence, quelques choses que j'ai considérées:
Je pourrais créer un SynchronizationContext personnalisé qui rassemble tous les appels de séquenceur pour la durée d'une séquence vers un seul thread. Cela a l'avantage de me permettre de réutiliser mon code de gestion d'accès par affinité de thread existant. L'inconvénient est que cela nécessitera de dédier un thread chaque fois que je fais une séquence ou une opération (car les opérations peuvent également s'étendre sur plusieurs threads.)
Créez un jeton d'accès pouvant être acquis pour passer aux méthodes d'opération afin de prouver que vous avez obtenu l'accès. Cela a l'inconvénient de gonfler les méthodes avec un paramètre de jeton.
Utilisez l'approche du jeton d'accès de (2), mais créez une implémentation d'interface en double pour l'interface Opérations afin qu'un wrapper puisse être instancié avec le jeton `` intégré ''. Cela crée un code glu laid, mais il nettoie le code du séquenceur de sorte qu'il n'a plus besoin de passer un jeton à chaque méthode.
Ma question: quelqu'un connaît-il un bon schéma pour gérer l'acquisition exclusive en présence de commutation de thread en raison de l'async/wait?
Oui, vous pouvez utiliser AsyncLock
, qui est également disponible dans le cadre de ma bibliothèque AsyncEx . Si vous souhaitez avoir une opération de type "TryLock", vous devrez peut-être créer votre propre primitive.
Vous perdez une partie de la capacité d'effectuer des vérifications de sécurité: il n'y a aucun moyen de vérifier si le thread en cours d'exécution a un AsyncLock
spécifique.
Les autres options incluent ConcurrentExclusiveSchedulerPair
(dont je parle ici ) ou TPL Dataflow.
Il y a SemaphoreSlim.WaitAsync
qui correspond étroitement ici. (Je l'ai trouvé dans ne question similaire ).