2009-09-09 10 views
4

Ok,Assassinat/Aborting un fil avec une seule tâche géante (par exemple SQL Query)

Il semble y avoir beaucoup de discussions ici sur la façon dont Thread.Abort est vraiment affreux() est, et comment travailler autour de l'utilisation de Thread.Abort() dans votre code en ajoutant des contrôles de sortie dans votre boucle.

Ceci suppose que vous contrôlez la façon dont le travail est partitionné d'une manière suffisamment granulaire pour qu'il soit bouclé dessus.

Mais comment puis-je faire ce qui suit:

  1. thread de travail de lancement pour faire quelque chose tout en gardant l'interface utilisateur réactive. Pour toutes fins utiles le code pour le thread de travail ressemble à ceci:

    public override void Run() 
    { 
        try 
        { 
         _dataTable = ExecuteSingleGinormousSqlQuery(); 
        } finally 
        { 
         // close the connection if it was left open. 
        } 
    } 
    
  2. Avez bouton « Annuler » afin que l'utilisateur peut interrompre cela s'ils se lassent d'attendre.

Il ne semble pas exister d'autre moyen de contourner cela qu'avec Thread.Abort(). Est-ce vrai? En outre, dans les tests empiriques, il ne semble même pas que Thread.Abort() tue réellement le thread jusqu'à l'appel qui effectue la requête, par ex.

new SqlDataAdapter(cmd).Fill(ds); 

revient en premier lieu, annulant ainsi de manière significative son utilité.

Y a-t-il un moyen de contourner cela? Les meilleures pratiques?

Répondre

1

Vous avez raison de dire qu'il n'abandonnera pas immédiatement un thread qui est occupé dans un appel interop, ce qui explique probablement pourquoi il ne s'arrête pas avant la fin de l'appel de la base de données. (Voir http://www.devx.com/codemag/Article/17442/0/page/4)

Je suppose que vous pouvez rendre l'appel asynchrone (voir http://www.devx.com/dotnet/Article/26747) mais tout cela signifie que vous n'avez pas besoin d'abandonner le thread. L'appel continue en arrière-plan et n'est pas abandonné à la fin de la base de données. Vous pourriez aussi bien utiliser un appel synchrone et laisser le thread compléter, en ignorant ses résultats. L'autre problème est que vous ne pouvez pas utiliser une méthode Fill simple pour faire le travail, si vous voulez aller asynchrone.

0

Vous êtes l'ingénieur. Personne ne vous jettera en prison pour avoir utilisé Abort().

Toutes les constructions dangereuses et/ou déconseillées ont des circonstances spécifiques où elles peuvent être souhaitables et parfois nécessaires. Cela peut être un tel cas. Maîtrise de toute compétence est de connaître les règles et savoir quand les briser.

Mais ... et c'est un gros mais ... la grande question est: que se passe-t-il lorsque `ExecuteSingleGinormousSqlQuery() 'est lui-même abandonné? Va-t-il laisser le client, le serveur ou la base de données dans un état foiré? Êtes-vous sûr vous savez ce qu'il va se passer? Avez-vous accès à tous le code source? Et même si vous le faites, avez-vous parcouru chaque ligne pour vous assurer que c'est sécuritaire?

Le problème avec Thread.Abort() est que avorte peut se produire partout , et nous savons qu'il ya eu des bugs dans le CLR qui peut rendre le code apparemment en sécurité se conduisent mal.

+2

La prison n'est pas la pire conséquence possible. http://xkcd.com/292/ –

1

Je tuerais la requête ou la connexion à la base de données.Cela peut être fait sur la plupart des serveurs via un simple appel de commande ou de procédure.

Vous pouvez facilement tuer requête dans mysql la commande suivante:

KILL QUERY <query_id> 

ou mssql

KILL <unit of work id> 
0

Je suis d'accord avec Larry. Tant que vous êtes à peu près sûr que la requête SQL ne gâchera pas la base de données et qu'elle ne semble pas poser des problèmes ailleurs dans votre application en étant désactivée, cela pourrait être une utilisation "acceptable" pour Abort. Toutefois, n'oubliez pas que même si vous supprimez le code SQL, s'il s'agit d'un fichier qui effectue des mises à jour ou des suppressions, il faudra encore un certain temps pour annuler les modifications et ne pas libérer vos tables immédiatement. En outre, vous pourriez mieux vous concentrer sur l'optimisation de la base de données à la place en optimisant la requête, en ajoutant des index, etc., pour que la requête Ginormous ne tourne pas aussi longtemps qu'elle semble l'être.

2

L'objet SqlCommand possède une méthode Cancel() que vous pouvez appeler à partir d'un autre thread pour tenter d'annuler une requête SQL en cours d'exécution. Il n'est pas garanti de réussir, et dépendra de la base de données, des pilotes et de la requête en cours d'exécution, mais vous pourriez trouver que c'est bon pour ce dont vous avez besoin.

0

Une autre option consiste à exécuter votre opération de longue durée dans un thread d'arrière-plan, mais vous l'exécutez dans un nouveau processus.

Il y a 2 inconvénients:

  1. Un processus a une pénalité de performance qui est de plusieurs ordres de grandeur supérieure à celle d'un fil. À la fois dans le temps de démarrage et dans les frais généraux de mémoire. Dans le cas d'un scénario d'interface utilisateur, cependant, le temps de démarrage d'un nouveau processus d'arrière-plan devrait être négligeable et peut être mesuré comme se produisant "en un clin d'œil".

  2. Vous devez utiliser une méthode de communication interprocessus pour renvoyer les résultats à votre programme principal, ce qui est généralement plus compliqué que le partage de la mémoire entre les threads.

L'avantage d'un processus par rapport fil est que la sémantique de tuer un processus sont bien compris, et vous êtes à peu près garantie que le processus mettra immédiatement fin et des ressources ne sera pas divulgué.

paix

0

Si vous ne me dérange pas refactorisation votre code, vous pouvez utiliser la méthode SqlCommand.BeginExecuteReader ou l'une des autres variantes asynchrones.