2017-07-31 2 views
-1

Hy All!C# DispatcherTimer qui ne fonctionne pas lorsque j'ai démarré dans la méthode ThreadPool.QueueUserWorkItem()

Je crée une classe TimerManager pour mon application WPF. Cette classe gère le démarrage et l'arrêt du temporisateur du répartiteur. Voici la classe:

public static class TimerManager 
{ 
    static DispatcherTimer disTimer; 
    static Model m = Model.GetInstance(); 
    static TimerManager() 
    { 
     disTimer = new DispatcherTimer(); 
     disTimer.Tick += disTimer_tick; 
     disTimer.Interval = new TimeSpan(0, 0, 1); 
    } 

    public static void StartTimer() 
    { 
     disTimer.Start(); 

    } 

    public static void StopTimer() 
    { 
     disTimer.Stop(); 
    } 

    private static void disTimer_tick(object sender, EventArgs e) 
    { 
     m.Tick++; 
    } 
} 

Et je créer une classe de modèle cela représente le tic-tac dans l'interface utilisateur. (Liaison dans MainWindow.xaml -> Champ de texte de la zone de texte xy {Tick Tick)}.

class Model : INotifyPropertyChanged 
{ 
    private Model() 
    { 

    } 

    static Model instance; 
    public static Model GetInstance() 
    { 
     if (instance == null) 
     { 
      instance = new Model(); 
     } 
     return instance; 
    } 

    int tick; 

    public event PropertyChangedEventHandler PropertyChanged; 
    public void OnNotifyPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName); 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, e); 
     } 
    } 

    public int Tick 
    { 
     get 
     { 
      return tick; 
     } 

     set 
     { 
      tick = value; 
      OnNotifyPropertyChanged(); 
     } 
    } 
} 

Et voici la classe principale:

Model m; 
    public MainWindow() 
    { 
     InitializeComponent(); 
     m = Model.GetInstance(); 
     this.DataContext = m; 
    } 

    private void startButton_Click(object sender, RoutedEventArgs e) 
    { 
     ThreadPool.QueueUserWorkItem(o => 
     { 
      TimerManager.StartTimer(); 
     }); 
     //TimerManager.StartTimer(); 
    } 

    private void stopButton_Click(object sender, RoutedEventArgs e) 
    { 
     TimerManager.StopTimer(); 
    } 

So .. Quand je cliqué sur le startButton i utiliser la méthode ThreadPool.QueueUserWorkItem ... car il est très important dans mon programme secret. Et dans cette méthode démarrer la minuterie et la coche de la minuterie n'est pas exécutée toutes les 1 seconde. Mais je n'utilise pas ThreadPool ..... etc c'est travaillé! Mais cette solution n'est pas bonne pour moi car ThreadPool est important pour moi car j'utilise un serveur web http (en local). Donc, ma question est: Le ticking est pourquoi ne pas travailler si j'utilise ThreadPool? Comment résoudre cela?

Au revoir, et merci pour tous :)

(Désolé mon mauvais anglais :()

+0

pourquoi utiliser ThreadPool pour presque instantané 'TimerManager.StartTimer();' opération? – ASh

+0

Parce que c'est un exemple simple. Mais dans mon application (sur commande) n'utilisez pas l'instant car j'utilise un httplistener (à partir de cette page [link] (https://codehosting.net/blog/BlogEngine/post/Simple-C-Web-Server)) méthode. J'utilise TimerManager.StartTimer() avant de renvoyer une réponse. –

+0

Cela aurait été mieux si vous aviez fourni un bon [mcve], y compris le XAML nécessaire. Mais il y a suffisamment d'informations ici pour savoir quel est le problème que vous avez. Voir ma réponse ci-dessous. –

Répondre

0

L'objet DispatcherTimer a une affinité de fil. Autrement dit, il est lié à un fil spécifique. En particulier, est conçu spécifiquement pour élever son événement Tick dans le fil dans lequel il a été créé, en utilisant le Dispatcher pour ce thread.

votre constructeur statique de la classe ThreadManager sera appelée lorsque le type est d'abord utilisé. dans votre exemple non travail, cela se produit dans l'élément de travail en file d'attente m ethod, provoquant l'exécution du constructeur statique dans le thread de pool de threads utilisé pour exécuter cette méthode d'élément de travail. Cela provoque à son tour l'objet DispatcherTimer que vous créez pour appartenir à ce thread et pour que son événement Tick soit déclenché dans ce thread par le Dispatcher pour ce thread. Sauf que les threads de pool de threads ne possèdent pas Dispatcher s. Il n'y a donc pas de Dispatcher pour déclencher l'événement Tick pour l'objet DispatcherTimer. Même s'il y avait, sans un appel à Application.Run() pour que la boucle dispatcher soit exécutée, le Dispatcher ne pourrait rien envoyer, y compris l'événement Tick. Ce dont vous avez besoin est de vous assurer que lorsque vous créez l'objet DispatcherTimer, le code qui crée cet objet est exécuté dans le thread Dispatcher, qui est le thread principal de votre interface utilisateur.

Il y a plusieurs façons de le faire. À mon humble avis, la meilleure façon est de faire votre ThreadManager classe pas une classe static et de créer une instance de celui-ci dans votre MainWindow constructeur.Par exemple:

class TimerManager 
{ 
    DispatcherTimer disTimer; 
    Model m = Model.GetInstance(); 

    public TimerManager() 
    { 
     disTimer = new DispatcherTimer(); 
     disTimer.Tick += disTimer_tick; 
     disTimer.Interval = new TimeSpan(0, 0, 1); 
    } 

    public void StartTimer() 
    { 
     disTimer.Start(); 
    } 

    public void StopTimer() 
    { 
     disTimer.Stop(); 
    } 

    private void disTimer_tick(object sender, EventArgs e) 
    { 
     m.Tick++; 
    } 
} 

et:

public partial class MainWindow : Window 
{ 
    TimerManager _timerManager = new TimerManager(); 

    public MainWindow() 
    { 
     InitializeComponent(); 
     this.DataContext = Model.GetInstance(); 
    } 

    private void startButton_Click(object sender, RoutedEventArgs e) 
    { 
     ThreadPool.QueueUserWorkItem(o => 
     { 
      _timerManager.StartTimer(); 
     }); 
    } 

    private void stopButton_Click(object sender, RoutedEventArgs e) 
    { 
     _timerManager.StopTimer(); 
    } 
} 

Puisque vous connaissez votre objet MainWindow doit être créé dans le thread répartiteur, et vous savez que l'initialisation du champ non statique se produit en même temps le constructeur est appelé, dans ce même thread Dispatcher, ce qui précède garantit que votre objet TimerManager est créé dans le thread Dispatcher.

Ceci vous donne un contrôle total sur la durée de vie de l'objet TimerManager, particulièrement quand il est créé mais bien sûr aussi quand il peut être jeté. Compte tenu de la nature de l'objet DispatcherTimer lui-même, je suis d'avis que c'est mieux que de maintenir une instance statique. Cette approche vous donne aussi la possibilité d'avoir un objet manager pour chaque thread de dispatcheur (dans de rares cas, un programme peut en avoir plus d'un, vous devriez essayer très fort pour éviter de vous retrouver dans cette situation, mais cela peut être utile pour que les types soient au moins compatibles avec une telle situation).

Cela dit, si vous vraiment voulez garder la mise en œuvre static, vous pouvez le faire en fournissant un procédé qui peut être appelé explicitement lorsque vous voulez initialiser la classe, de sorte que vous pouvez vous assurer que l'initialisation se passe dans le fil droit:

static class TimerManager 
{ 
    static DispatcherTimer disTimer; 
    static Model m = Model.GetInstance(); 

    public static void Initialize() 
    { 
     disTimer = new DispatcherTimer(); 
     disTimer.Tick += disTimer_tick; 
     disTimer.Interval = new TimeSpan(0, 0, 1); 
    } 

    public static void StartTimer() 
    { 
     disTimer.Start(); 
    } 

    public static void StopTimer() 
    { 
     disTimer.Stop(); 
    } 

    private static void disTimer_tick(object sender, EventArgs e) 
    { 
     m.Tick++; 
    } 
} 

Puis dans votre MainWindow classe:

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     this.DataContext = Model.GetInstance(); 
     StaticTimerManager.Initialize(); 
    } 

    private void startButton_Click(object sender, RoutedEventArgs e) 
    { 
     ThreadPool.QueueUserWorkItem(o => 
     { 
      StaticTimerManager.StartTimer(); 
     }); 
    } 

    private void stopButton_Click(object sender, RoutedEventArgs e) 
    { 
     StaticTimerManager.StopTimer(); 
    } 
} 

Tout ce que vous devez faire ici est de vous assurer que vous appelez le Initialize() rencontré hod du thread d'interface utilisateur principal où vous avez réellement un répartiteur en cours d'exécution, avant vous essayez d'appeler l'une des deux autres méthodes static dans la classe. Cette approche pourrait également être faite pour fonctionner avec plusieurs threads (c'est-à-dire si vous avez plus d'un thread Dispatcher), mais ce serait plus compliqué, surtout si vous voulez pouvoir appeler la méthode StartTimer() à partir d'un thread différent effectivement possède l'objet de minuterie. Je recommanderais contre l'approche de classe static si vous avez vraiment terminé dans cette situation.

+0

Merci :) Je crée une classe TimerManager singleton mais si j'initialise une instance dans threadpool.queueuserworkitem ne fonctionne pas le ticker timer mais si je crée une instance dans le constructeur mainwindow cela fonctionne :) Et je crée une classe StaticTimerManager (je préférais cette solution) avec "initialiser" la méthode et exécuter cette méthode dans le pool de threads ... ne fonctionne pas le ticking, mais je cours cette méthode (initialiser) dans mainwindow (ou autre) tique constructeur fonctionne :) Merci! :) Mais pourquoi? Puis-je m'initialiser dans un pool de threads avec un ticking fonctionnel? : \ –

+0

_ "Puis-je m'initialiser dans le pool de threads avec un ticking fonctionnel?"_ - non, vous ne pouvez pas, et pour la raison que j'ai expliquée au début de la réponse affichée ci-dessus Si vous allez utiliser la classe' DispatcherTimer', vous devez ** créer ** l'objet 'DispatcherTimer' * * dans le fil d'interface utilisateur qui le possédera ** –

+0

Jep. Thx, je comprends que :) J'ai ajouté un "tuyau vert" pour votre réponse :) –