Comment faire un C# timer qui déclenche des événements dans le thread principal?
Longue histoire courte, j'ai besoin d'une précision de temporisation .Net - avec prescision en millisecondes - sens, si je lui dis de mettre le feu à un événement lors de 10ms passe, il doit le faire, à +-1ms. L'intégré .Net classe Timer a une précision de +-16 ms il me semble, ce qui est inacceptable pour mon application.
J'ai trouvé cet article http://www.codeproject.com/Articles/98346/Microsecond-and-Millisecond-NET-Timer qui fournit un code pour une minuterie, ce qui est exactement ce dont j'ai besoin (même plus - que a la précision en microsecondes).
Cependant, le problème est, la OnTimer équivalent semble être exécutée dans un autre thread. Donc, si j'ai ajouter un peu de code qui ne, dire:
label1.Text = "Hello World";
Je vais avoir une exception, et donc je vais devoir le fait de l'écrire comme ceci:
Invoke( new MethodInvoker(() =>{label1.Text = "Hello World";}));
C'est, d'après ce que je comprends, parce que l'événement OnTimer est tiré de la minuterie fil où le temps est passé jusqu'à ce que suffisamment a réussi à être au-dessus de l'Intervalle, puis cliquez sur suivant OnTimer événement est déclenché. L' .Net de la Minuterie n'a pas ce problème - OnTimer de la .Net Timer, je peut librement modifier les commandes des membres.
Question: Que dois-je changer pour que mon minuteur va exécuter l'événement OnTimer dans le thread principal? Est l'ajout de "Invoquer" le seul choix?
- La seule raison pour laquelle les Formes de la minuterie ne souffre pas c'est parce qu'il Invoque en votre nom à titre de commodité. Si vous utilisez d'autres compteurs à zéro, vous devez Appeler comme vous le décrivez dans votre question.
- Il est probablement utile de rappeler que Windows est pas un système d'exploitation temps réel. En essayant de faire quelque chose arrive à 1ms, la précision peut être délicat.
- Pourquoi avez-vous besoin d'un tel débit? Vous ne seriez jamais capable de mettre à jour l'INTERFACE utilisateur rapide. De toute façon, les windows forms de la minuterie est le chemin à parcourir. C'est un wrapper pour un OS timer qui déclenche le thread d'INTERFACE utilisateur, et c'est la chose la plus rapide que vous trouverez qui le fait.
- Je me demande pourquoi vous avez besoin d'une précision de 1 milliseconde pour la modification d'une étiquette si votre écran a une latence de quelques millisecondes.
- Oui j'ai un peu compris, dans le processus de l'écriture du code, que, même à 120Hz moniteur nous avons encore un GUI de précision de 8ms, car nous ne pouvons changer les cadres, chaque 8ms. Donc, il n'y a pas beaucoup de point à la lutte pour la précision dans l'INTERFACE utilisateur de la partie. Cependant, il n'est pas seulement à propos de l'INTERFACE utilisateur, de sorte que je vais encore avoir une utilisation d'un précis de la minuterie.
- Si c'est un fait que .Net Minuterie juste "invoque" pour moi, la question est résolue, va avoir à invoquer. Je vais aussi essayer la solution ci-dessous qui a obtenu le plus de votes.
Vous devez vous connecter pour publier un commentaire.
Bien qu'il existe plusieurs façons de le faire, celui que je préfère en général est d'avoir la minuterie de capture de la valeur de
SynchronizationContext.Current
quand il est créé. Cette valeur, lorsque dans le thread de l'INTERFACE utilisateur, contiennent de l'actuel contexte de synchronisation qui peut être utilisé pour exécuter des méthodes dans la boucle de message quand, dans un contexte de l'INTERFACE utilisateur. Cela fonctionne pour winforms, WPF, silverlight, etc. Tous ces paradigmes de définir un contexte de synchronisation.Il suffit de saisir cette valeur lorsque la minuterie est créé, en supposant il est créé dans le thread de l'INTERFACE utilisateur. Si vous voulez avoir une option du constructeur d'/propriété pour définir la valeur de sorte que vous pouvez l'utiliser même si la minuterie n'a pas été créé dans le thread de l'INTERFACE utilisateur vous pouvez, bien que cela ne devrait pas être nécessaire la plupart du temps.
Alors utilisez simplement la
Send
méthode de ce contexte pour déclencher l'événement:Post
serait mieux. AvecSend
vous perdez votre temps sur la plus grande partie inutile de la synchronisation. Encore, les windows forms minuterie donnera de meilleurs résultats, car il ne nécessite pas de synchronisation à tous - il directement envoie un message à la fenêtre au lieu de lancer des threads du pool de threads.Send
ouPost
est de savoir si vous voulez la minuterie à attendre pour les gestionnaires à tous, avant de tirer plus, ou pas. Comme pour l'utilisation de l'autre minuterie; l'OP a spécifiquement déclaré que ce n'est pas une option pour lui dans ce contexte, et qu'il doit utiliser son propre minuterie alors que les commissaires de la thread de l'INTERFACE utilisateur. C'est exactement ce que fait.Il n'y a pas moyen de contourner le dispatching Élément de l'INTERFACE utilisateur l'accès au thread principal. Si la mise à jour d'un élément d'INTERFACE est vraiment la seule chose que vous l'intention de faire dans la minuterie de rappel oublier sur votre minuterie exigence de précision. L'utilisateur ne voit pas la différence entre les 16ms et 50ms.
Mener à bien les critiques de temps de travail dans votre minuterie de rappel et d'expédier le reste de l'INTERFACE de travail pour le thread principal:
Dans wpf, vous pouvez utiliser le répartiteur classe à envoyer des messages dans le thread de l'INTERFACE utilisateur:
En winforms, vous devez appeler la invoquer méthode: