2017-02-10 1 views
11

Dans Haskell, nous avons des exceptions asynchrones; nous pouvons utiliser throwTo pour soulever une exception dans un autre thread:Est-ce que Python a un équivalent des fonctions 'mask' ou 'bracket' de Haskell?

throwTo :: Exception e => ThreadId -> e -> IO() 

throwTo déclenche une exception arbitraire dans le thread cible (GHC uniquement).

Pour pouvoir écrire du code avec des garanties comme « toujours libérer un verrou après l'avoir acquis », nous avons mask d'exécuter du code dans lequel des exceptions asynchrones ne peuvent être reçues alors que le calcul bloque:

mask :: ((forall a. IO a -> IO a) -> IO b) -> IO b 

Exécute un calcul d'E/S avec des exceptions asynchrones masqué. Autrement dit, tout thread qui tente de déclencher une exception dans le thread en cours avec throwTo sera bloqué jusqu'à ce que les exceptions asynchrones soient à nouveau masquées.

et une plus forte uninterruptibleMask où des exceptions async ne seront pas soulevées du tout lors d'un calcul masqué:

uninterruptibleMask :: ((forall a. IO a -> IO a) -> IO b) -> IO b 

Comme mask, mais le calcul masqué n'est pas interruptible

Le masquage est utilisé pour implémenter des abstractions de niveau supérieur telles que bracket:

bracket 
    :: IO a   -- computation to run first ("acquire resource") 
    -> (a -> IO b) -- computation to run last ("release resource") 
    -> (a -> IO c) -- computation to run in-between 
    -> IO c   -- returns the value from the in-between computation 

Lorsque vous souhaitez acquérir une ressource, faire un travail avec elle, puis relâchez la ressource, il est une bonne idée d'utiliser bracket, car bracket installera le gestionnaire d'exceptions nécessaires pour libérer la ressource dans le cas où une exception est soulevée lors du calcul. Si une exception est déclenchée, alors bracket relance l'exception (après l'exécution de la version).

Si je comprends bien, Python a une (moins générale) sous forme d'exceptions asynchrones, avec la manifestation la plus notable étant KeyboardInterrupt:

Raised lorsque l'utilisateur appuie sur la touche d'interruption (normalement contrôle - C ou Supprimer). Pendant l'exécution, une vérification des interruptions est faite régulièrement.

La documentation est imprécise quand le « contrôle des interruptions » peut se produire, mais il semble impliquer qu'un KeyboardInterrupt peut être soulevée à tout moment dans l'exécution d'un programme. Il semble donc que les exceptions asynchrones de Python ont toutes les mêmes difficultés subtiles à maintenir la rectitude.

Par exemple, considérons un modèle comme celui-ci:

x = None 
try: 
    x = acquire() 
    do_something(x) # (1) 
finally: 
    if x is not None: # (2) 
     release(x) 

Si une exception est soulevée au cours de (1), nous sommes assurés que le contenu du bloc finally seront exécutés. Mais que se passe-t-il si un KeyboardInterrupt est pendant (2)?

Il semble fondamentalement impossible de garantir le nettoyage des ressources en présence d'exceptions asyc sans un moyen de les masquer. Y a-t-il une facilité pour cela, ou est-ce que nous comptons sur le ostrich algorithm?

+1

Je pense que vous devez pratiquement installer votre propre gestionnaire de signal pour Ctrl-C si vous voulez faire cela. – user2357112

Répondre

3

C'est ce que les gestionnaires de contexte sont pour.

with acquire() as x: 
    do_something(x) 

acquire renvoie un objet dont le type définit une méthode __enter__, qui retourne la valeur liée à x, et un procédé __exit__, qui est exécuté à la fin de l'instruction with quel que soit l'état est quitté.

+2

La méthode '__exit__' peut-elle être interrompue si une exception supplémentaire se produit? –

+0

@DanielWagner Mon vague souvenir d'antan quand j'ai écrit Python: les exceptions qui se produisent pendant '__exit__' interrompent la méthode' __exit__' comme d'habitude; l'exception qui a provoqué le bloc '__exit__' devient l'option '__cause__' de l'exception suivante. –

+0

Vous devez installer un gestionnaire de signal vous-même, mais la méthode '__exit__' fournit une portée pratique pour cela: installez le gestionnaire immédiatement après avoir entré la méthode et restaurez le gestionnaire précédent avant de le quitter. – chepner