Je cherche un moyen de garantir que chaque fois qu'un thread verrouille une ressource spécifique, il est obligé de libérer cette ressource après une période de temps spécifique (s'il ne l'a pas déjà publiée). Imaginez une connexion où vous devez limiter la durée pendant laquelle un thread spécifique peut posséder cette connexion.
J'imagine que c'est ainsi qu'il pourrait être utilisé:
{
std::lock_guard<std::TimeLimitedMutex> lock(this->myTimeLimitedMutex, timeout);
try {
// perform some operation with the resource that myTimeLimitedMutex guards.
}
catch (MutexTimeoutException ex) {
// perform cleanup
}
}
Je vois qu'il y a un timed_mutex qui laisse le programme expirer si un verrou ne peut pas être acquis. J'ai besoin du délai d'expiration après le verrou est acquis.
Il existe déjà des situations où vous obtenez une ressource qui peut être supprimée de façon inattendue. Par exemple, un socket TCP - une fois la connexion socket établie, le code de chaque côté doit gérer le cas où l'autre côté abandonne la connexion.
Je recherche un modèle qui gère les types de ressources qui expirent normalement d'eux-mêmes, mais lorsqu'ils ne le font pas, ils doivent être réinitialisés. Cela ne doit pas gérer tous les types de ressources.
Cela ne peut pas fonctionner et cela ne fonctionnera jamais. En d'autres termes, cela ne peut jamais être fait. Cela va à l'encontre de tout concept de propriété et de transactions atomiques. Parce que lorsque thread acquiert le verrou et implémente deux transactions d'affilée, il s'attend à ce qu'elles deviennent atomiquement visibles à l'extérieur de Word. Dans ce scénario, il serait très possible que la transaction soit déchirée - la première partie sera effectuée, mais pas la seconde.
Ce qui est pire, c'est que puisque le verrou sera supprimé de force, la transaction partiellement exécutée deviendra visible à l'extérieur de Word, avant que le thread interrompu ait la moindre chance de revenir en arrière.
Cette idée va à l'encontre de toute école de pensée multithread.
Je soutiens la réponse de SergeyAs. La libération d'un mutex verrouillé après une temporisation est une mauvaise idée et ne peut pas fonctionner. Mutex est synonyme d'exclusion mutuelle et il s'agit d'un contrat extrêmement difficile qui ne peut être violé.
Mais vous pouvez faire ce que vous voulez:
Problème: Vous voulez garantir que vos threads ne tiennent pas le mutex plus longtemps qu'un certain temps T.
Solution: Ne verrouillez jamais le mutex plus longtemps que le temps T. Au lieu de cela, écrivez votre code afin que le mutex ne soit verrouillé que pour les opérations absolument nécessaires. Il est toujours possible de donner un tel temps T (modulo les incertitudes et limites étant donné mon système d'exploitation multitâche et multi-utilisateur bien sûr).
Pour y parvenir (exemples):
Il existe des exceptions à ces règles, mais la ligne directrice générale est la suivante:
(*) Ce ne sont que des exemples d'opérations où il est tentant de verrouiller la liste entière, d'effectuer les opérations puis de déverrouiller la liste. Au lieu de cela, il est conseillé de simplement prendre une copie locale de la liste et d'effacer la liste d'origine pendant que le mutex est verrouillé, idéalement en utilisant l'opération swap()
offerte par la plupart des conteneurs STL. Effectuez ensuite l'opération lente sur la copie locale en dehors de la section critique. Ce n'est pas toujours possible mais vaut toujours la peine d'être considéré. Le tri a une complexité carrée dans le pire des cas et nécessite généralement un accès aléatoire à la liste entière. Il est utile de trier (une copie de) la liste en dehors de la section critique et de vérifier ultérieurement si des éléments doivent être ajoutés ou supprimés. Les allocations de mémoire ont également une certaine complexité derrière elles, donc les allocations/désallocations massives de mémoire doivent être évitées.
Vous ne pouvez pas faire cela avec seulement C++.
Si vous utilisez un système Posix, cela peut être fait. Vous devrez déclencher un signal SIGALARM qui n'est démasqué que pour le thread qui va expirer. Dans le gestionnaire de signaux, vous devrez définir un indicateur et utiliser longjmp
pour revenir au code de thread. Dans le code de thread, à la position setjmp
, vous ne pouvez être appelé que si le signal a été déclenché, vous pouvez donc lever l'exception Timeout
.
Veuillez voir cette réponse pour savoir comment procéder.
De plus, sous linux, il semble vous pouvez lancer directement depuis le gestionnaire de signal (donc pas de longjmp/setjmp ici).
BTW, si j'étais vous, je coderais le contraire. Pensez-y: vous voulez dire à un fil "hé, vous prenez trop de temps, alors jetons tout le (long) travail que vous avez fait jusqu'à présent pour que je puisse progresser". Idéalement, votre long fil devrait être plus coopératif, faire quelque chose comme "J'ai fait A d'une tâche ABCD, libérons le mutex pour que d'autres puissent progresser sur A. Ensuite, vérifions si je peux le reprendre pour faire B et bientôt." Vous voulez probablement être plus fin (avoir plus de mutex sur des objets plus petits, mais assurez-vous de verrouiller dans le même ordre) ou utiliser des verrous RW (afin que d'autres threads puissent utiliser les objets si vous ne les modifiez pas), etc...
Une telle approche ne peut pas être appliquée car le titulaire du mutex doit avoir la possibilité de nettoyer tout ce qui est laissé dans un état non valide pendant la transaction. Cela peut prendre un temps arbitraire inconnu.
L'approche typique consiste à libérer le verrou lorsque vous effectuez de longues tâches et à le réacquérir au besoin. Vous devez gérer cela vous-même car tout le monde aura une approche légèrement différente.
La seule situation que je connaisse où ce genre de chose est une pratique acceptée est au niveau du noyau, en particulier en ce qui concerne les microcontrôleurs (qui n'ont pas de noyau, ou sont tous du noyau, selon la personne à qui vous demandez). Vous pouvez définir une interruption qui modifie la pile d'appels, de sorte que lorsqu'elle est déclenchée, elle déroule les opérations particulières qui vous intéressent.
Les variables "condition" peuvent avoir des délais d'expiration. Cela vous permet d'attendre qu'un thread volontairement libère une ressource (avec notify_one () ou notify_all ()), mais l'attente elle-même expirera après un délai spécifié durée fixe.
Des exemples dans la documentation de Boost pour les "conditions" pourraient rendre cela plus clair.
Si vous voulez forcer une version, vous devez cependant écrire le code qui la forcera. Cela pourrait être dangereux. Le code écrit en C++ peut faire des trucs assez proches du métal. La ressource pourrait accéder à du matériel réel et attendre qu'elle finisse quelque chose. Il peut ne pas être physiquement possible de mettre fin à tout ce que le programme est bloqué.
Cependant, si cela est possible, vous pouvez le gérer dans le thread dans lequel l'attente () expire.