2010-10-06 8 views
1

VB.NET 2010, .NET 4VB.NET Abandonner un appel de méthode asynchrone après le délai

Bonjour à tous,

J'ai un objet System.Timers.Timer qui fait un travail sur son événement écoulé:

Private Sub MasterTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles MasterTimer.Elapsed 
    MasterTimer.Enabled = False 
    '...work... 
    MasterTimer.Enabled = True 
End Sub 

Mon problème est que le travail que ça fait est parfois bloqué. Une partie du travail est la communication en série, donc il pourrait être bloqué en attente d'une réponse de quelque chose. J'ai déjà modifié un peu mon code de communication en série pour, espérons-le, résoudre le problème. Cependant, cette minuterie est fondamentalement le battement de coeur d'une application de contrôle de production et il est très mauvais si elle devait s'arrêter pour une raison quelconque. Je pensais qu'il serait peut-être bon de mettre un time-out à sécurité intrinsèque de sorte que, si le "travail" prend trop de temps, le minuteur puisse se réactiver et essayer à nouveau. Je pensais à quelque chose comme ceci:

Déplacer le travail dans un sous-programme et créer un délégué:

Private Delegate Sub WorkDelegate() 
Private Sub Work() 
    '...work... 
End Sub 

Appelez le travail en invoquant le délégué puis utilisez WaitOne (délai d'attente) sur le IAsyncResult pour spécifier un Mais, ma question est: Est-ce que cela causerait un problème si Work() était réellement coincé quelque part? En cela, il ré-entrerait un sous-programme qui est déjà en cours d'exécution? Existe-t-il un moyen d'abandonner Work() s'il n'a pas terminé après le timeout? En d'autres termes, il suffit de cesser l'exécution de Work() si result.IsCompleted est False après WaitOne?

Je ne comprends pas vraiment très bien ce genre de choses, donc tous les commentaires seraient grandement appréciés. Peut-être y a-t-il une façon totalement différente d'aborder cela que je ne connais pas?

Merci beaucoup d'avance!

Je voudrais ajouter quelque chose:

Bien que je compte faire quelques réécritures selon les suggestions de Hans, je l'avais déjà prévu une journée d'essais pour tenter d'isoler la source de cette erreur. Jusqu'à présent, cela ne s'est produit que lors de l'exécution de l'application compilée. J'ai passé aujourd'hui (et certains hier) à essayer de reproduire le gel en mode débogage afin que je puisse ajouter quelques points d'arrêt et comprendre ce qui se passe. Jusqu'à présent, le programme n'a pas gelé en mode débogage. Je me demande simplement s'il y a quelque chose de différent dans l'environnement de débogage qui pourrait expliquer cela. C'est peut-être juste que je suis "chanceux", mais je l'ai exécuté probablement trois fois la durée moyenne après laquelle le programme a gelé lors de l'exécution de l'exécutable ... Encore une fois, je suis assez ignorant, mais y at-il quelque chose? unique à propos de l'environnement de débogage qui pourrait expliquer cela? Je mettrai à jour à nouveau s'il se bloque.

Répondre

2

Cela se passe mal sur la toute première ligne du code. La classe System.Timers.Timer est assez icky, il n'y a absolument aucune garantie que l'appel Stop() empêchera un autre appel. Le minuteur utilise ThreadPool.QueueUserWorkItem pour effectuer l'appel du gestionnaire d'événements Elapsed. Si le pool de threads est occupé, cela peut entraîner la mise en file d'attente de plusieurs appels, en attendant d'obtenir le feu vert du planificateur TP pour démarrer. L'arrêt de la minuterie n'empêche pas les threads en attente de s'exécuter. Sans utiliser de verrou, ces threads vont se taper les uns sur les autres et gâcher votre état de communication.

Une valeur sûre est System.Threading.Timer avec une période de zéro, donc vous n'obtiendrez qu'un seul rappel. Rechargez le minuteur avec sa méthode Change().

L'appel de la méthode BeginInvoke() d'un délégué, puis le blocage de son achèvement n'a aucun sens. Appelez simplement Invoke(). Vous évite de brûler un autre fil.

Oui, si la méthode 'Travail' ne revient jamais, cela signifie que vous avez un problème. Un insoluble.

Une grande partie de cette misère pourrait disparaître si vous évitez d'utiliser l'interrogation pour voir si quelque chose est disponible sur le port série. Laissez-le vous dire qu'il y a quelque chose de valable. Il déclenche l'événement DataReceived, sur un thread threadpool, chaque fois qu'il y a au moins un octet dans le tampon de réception. L'utilisation de sa propriété WriteTimeout est également un excellent moyen d'éviter de rester coincé lorsque quelque chose ne va pas avec le protocole de communication ou le périphérique. Consacrer un thread et bloquer les appels Read fonctionne bien aussi.

+0

Merci pour la réponse. Hmmm, c'est un problème. J'ai lu à propos de la minuterie dans l'espace de noms Threading il y a un moment. Je pense que j'ai décidé d'aller avec le System.Timers.Timer parce qu'il semblait plus simple à utiliser et parce que je n'ai jamais plus de quelques threads actifs dans mon application à un moment donné. Mon intervalle de minuterie est 500ms. Compte tenu de cela, pensez-vous que les problèmes avec plusieurs threads Work() se chevaucheraient probablement. Peu importe, votre point est pris. Je vais essayer de réorganiser les choses en utilisant le Threading.Timer. –

+0

Encore une chose cependant ... Y a-t-il un moyen de tout emballer dans une sorte de délai d'attente sûr qui forcerait le temporisateur à arrêter immédiatement son travail, à se «décoller» et à recommencer. –

+0

Je ne suis pas sûr d'avoir compris le point. Il n'y a pas de 'brush off' si la minuterie ne peut tirer qu'une seule fois. Mais mon but était de regarder ce que SerialPort peut faire et de reconsidérer l'idée du sondage. Polling suce, il brûle des cycles et des threads inutiles et son dur à garder l'état. SerialPort.DataReceived évite l'interrogation mais pas l'état. Le blocage des appels lus ne nécessite pas d'état, mais brûle un thread. Je sais, difficile quand quelqu'un vous dit que vous devriez réécrire le code à partir de zéro. –

Questions connexes