我在ThreadPool.QueueUserWorkItem()方法中启动时,C#DispatcherTimer滴答不起作用

时间:2017-07-31 18:57:00

标签: c# .net wpf timer dispatchertimer

Hy All!

我为我的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++;
    }
}

我创建了一个Model类,它代表了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();
    }

所以.. 当我点击startButton时,我使用ThreadPool.QueueUserWorkItem ...方法因为在我的秘密程序中非常重要。 并且在该方法中启动计时器并且计时器滴答不是每1秒运行一次。 但我不使用ThreadPool .....等这是有效的!但是这个解决方案对我不利,因为ThreadPool对我很重要,因为我使用的是http网络服务器(在本地)。

所以,我的问题是:如果我使用ThreadPool,滴答作响是为什么不工作?怎么解决?

再见,而且对所有人来说都是:)

(抱歉,我的英文不好:()

1 个答案:

答案 0 :(得分:0)

DispatcherTimer对象具有线程关联性。也就是说,它与特定的线程相关联。特别是,它专门用于在创建它的线程中使用Tick为该线程提升Dispatcher事件。

首次使用类型时,将调用ThreadManager类的静态构造函数。在非工作示例中,这发生在排队的工作项方法中,导致静态构造函数在用于执行该工作项方法的线程池线程中执行。这反过来导致您创建的DispatcherTimer对象被该线程拥有,并且该线程的Tick在该线程中引发其Dispatcher事件。

除了,线程池线程没有Dispatcher s。因此,没有Dispatcher来提升Tick对象的DispatcherTimer事件。即使没有调用Application.Run()来执行调度程序循环,Dispatcher实际上也不会发送任何内容,包括Tick事件。

您需要确保在创建DispatcherTimer对象时,创建该对象的代码在调度程序线程中执行,该线程是您的主UI线程。

有几种方法可以做到这一点。恕我直言,最好的方法是让你的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对象本身的性质,我认为这比维护静态实例更好。

这种方法还为您提供了为每个调度程序线程提供管理器对象的选项(在极少数情况下,程序可能有多个...您应该尽力避免陷入这种情况,但它可能对类型至少与这种情况兼容)。

那就是说,如果确实希望保留static实现,你可以通过提供一个可以在你想要初始化类时显式调用的方法来做到这一点,所以你可以确保初始化发生在正确的线程中:

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

然后在你的MainWindow课程中:

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

您需要在此处执行的操作是确保从实际拥有正在运行的调度程序的主UI线程中调用Initialize()方法,之前尝试调用其他任何一个类中有两个static方法。

这种方法也可以用于多个线程(即如果你有多个调度程序线程),但它会更棘手,特别是如果你想能够从一个调用StartTimer()方法实际上拥有计时器对象的不同线程。如果你确实在那种情况下结束了,我建议不要使用static类方法。