2017-07-31 7 views
-1

Hy All!ThreadPool.QueueUserWorkItem()メソッドを起動したときにDispatcherTimerが動作しなくなりました

自分のWPFアプリケーション用のTimerManagerクラスを作成します。 このクラスは、ディスパッチャタイマの開始と停止を処理します。

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++; 
    } 
} 

と私は、これはUIで刻々と過ぎを表すモデルクラスを作成します。ここで クラスです。 (MainWindow.xaml - > xyテキストボックステキストフィールド{Binding 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(); 
     } 
    } 
} 

そして、ここでの主なクラスです:

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(); 
    } 

..だから 私は私の秘密のプログラムに非常に重要ですので、私はThreadPool.QueueUserWorkItem ...メソッドを使用してスタートボタンをクリックしたとき。 その方法では、タイマーを開始し、タイマーティックは1秒ごとに実行されません。 しかし、私はThreadPool .....などを使用しないでこれは働いている!しかし、私がhttpウェブサーバー(ローカル)を使用しているので、ThreadPoolが私のために重要であるので、この解決策は私にとってはうまくいかない。

私の質問は次のとおりです。私がThreadPoolを使用しているのはどうしてですか?それを解決するには?

さようなら、そしてすべての:)

ためのTHX(申し訳ありませんが、私の悪い英語:()

+0

ほとんどの 'TimerManager.StartTimer();'操作でThreadPoolを使う理由は何ですか? – ASh

+0

これは簡単な例です。しかし、私のアプリでは(https://codehosting.net/blog/BlogEngine/post/Simple-C-Web-Server)このページ(https://codehosting.net/post/Simple-C-Web-Server)からhttplistenerを使用しているため、インスタントを使用しないでください。方法。 応答を返す前にTimerManager.StartTimer()を使用します。 –

+0

必要なXAMLを含め、優れた[mcve]を提供した方が良いでしょう。しかし、ここにあなたが持っている問題が何であるかを知るのに十分な情報があります。下の私の答えを見てください。 –

答えて

0

DispatcherTimerオブジェクトがスレッドの親和性を持っている。それは、それが特定のスレッドに関連付けられている、である。特に、そのスレッドのDispatcherを使用して、それが作成されたスレッドにそのTickイベントを発生させるために特別に設計されています。タイプが最初に使用されたときに

あなたThreadManagerクラスの静的コンストラクタは、あなた以外の実施例では。と呼ばれます、これは、待ち行列に入れられた作業項目mそのワークアイテムメソッドの実行に使用されるスレッドプールスレッドで静的コンストラクタを実行させます。これにより、作成するDispatcherTimerオブジェクトがそのスレッドによって所有され、TickイベントがそのスレッドでそのスレッドのDispatcherによって生成されます。

を除き、スレッドプールスレッドにはDispatcherがありません。だからDispatcherTimerオブジェクトのTickイベントを発生させるためにそこにはDispatcherイベントがありません。 Application.Run()を呼び出すことなくディスパッチャループを実行する場合でも、Tickイベントを含むDispatcherは実際には何もディスパッチされません。 オブジェクトを作成するときに、そのオブジェクトを作成するコードがメインのUIスレッドであるディスパッチャスレッドで実行されるようにする必要があります。

これを行うにはいくつかの方法があります。 IMHO、最良の方法はThreadManagerクラスstaticクラスではなく、MainWindowコンストラクタでインスタンスを作成することです。たとえば、次のように

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++; 
    } 
} 

と:

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(); 
    } 
} 

あなたのMainWindowオブジェクトはディスパッチャースレッドで作成する必要があります知っている、とあなたは非静的フィールドの初期化が同時にコンストラクタを起こることを知っているので、が同じディスパッチャスレッドで呼び出された場合は、上記のようにして、TimerManagerオブジェクトがディスパッチャスレッドで確実に作成されます。

これは、TimerManagerオブジェクトのライフタイムを完全に制御します。特に、オブジェクトが作成されたときにはもちろん、廃棄することもできます。 DispatcherTimerオブジェクトの性質上、静的に保持されているインスタンスを維持するよりも優れていると私は考えます。

このアプローチでは、ディスパッチャスレッドごとにマネージャオブジェクトを持つこともできます(まれに、プログラムに&hellipが複数ある可能性があります。その状況に陥るのを防ぐために非常に努力する必要がありますが、少なくともそのような状況と互換性があるタイプのために)。あなた本当にstatic実装を維持したい場合は、あなたがクラスを初期化したい時に明示的に呼び出すことができる方法を提供することによって、それを行うことができますので、あなたは必ず初期化がで起こることを確認することができ、言っ

右のスレッド:あなたのMainWindowクラスで次に

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++; 
    } 
} 

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(); 
    } 
} 

あなたがここにする必要があるすべてはあなたがInitialize()が満たさ呼び出すことを確認していますあなたが実際に実行中のディスパッチャを持っているメインのUIスレッドから、の前にのいずれかの他の2つのstaticメソッドを呼び出そうとしました。

このアプローチは、複数のスレッド(つまり複数のディスパッチャスレッドがある場合)でも動作する可能性がありますが、特に、StartTimer()メソッドを別のスレッドから呼び出せるようにしたい場合は扱いにくいでしょう。実際にはのタイマオブジェクトを所有しています。あなたが本当にそのような状況に巻き込まれた場合、私はstaticクラスのアプローチに対してお勧めしたいと思います。

+0

ありがとうございました:) 私はシングルトンのTimerManagerクラスを作成しますが、私はtimerpool.queueuserworkitemのインスタンスを初期化する場合は、タイマーを動かさないが、メインウィンドウのコンストラクタでインスタンスを作成すればそれは動作します:) そして私はStaticTimerManagerクラスを作成しますこのメソッドを "initialize"メソッドで実行し、そのメソッドをスレッドプールで実行します... tickingは動作しませんが、mainwindow(またはその他の)コンストラクタでそのメソッド(初期化)を実行します。 :) しかし、なぜですか? 作業中のティッキングでスレッドプールで初期化できますか? :\ –

+0

_ "うまく働いてスレッドプールで初期化できますか?"DispatcherTimer'クラスを使用する場合、** DispatcherTimer'オブジェクトを作成しなければなりません。*あなたは、上記の答えの冒頭で説明した理由で、 *それを所有するUIスレッドで。 –

+0

Jep。 Thx、私はそれを理解しています:) 私はあなたの答えのための "緑色のパイプ"を追加しました:) –

関連する問題