如果中间发生另一个事件,调度程序计时器如何在tick事件中执行其工作?这段代码有什么问题

本文关键字:事件 段代码 工作 代码 什么 问题 执行 另一个 中间 调度程序 计时器 | 更新日期: 2023-09-27 17:59:52

在过去的几天里,我一直在看DispatcherTimer,但我仍然无法理解一些东西。这是我目前所理解的,

  1. tick 事件不会同时发生两次链接
  2. 无需担心对象的所有者线程由于调度程序计时器自动执行UI 线程
  3. 即时报价的时间可能不是很准确,因为即时报价基本上是从队列中执行的

现在我不清楚的是,如果在tick事件之间运行另一个事件,则代码的执行顺序。我有一个测试WPF应用程序,它使用调度程序计时器,其tick事件执行2个功能。 按顺序firstStep()secondStep()

firstStep()将变量设置为 null,而secondStep()将其设置为非 null 值。设置值后,secondStep(( 将开始一个具有 Completed 事件的情节提要,该事件尝试访问此变量。

所以我的问题是,如果我们保持计时器运行,Finished 事件是否有可能出现在 firstStep()secondStep() 函数之间?我已经编写了一个测试应用程序,似乎是这种情况,最终我们将达到执行 Completed 事件时变量为 null 的状态。但我不明白这是怎么发生的,因为firstStep()secondStep()按顺序执行,应该不可能在 2 个函数之间执行 Finished 事件(或者我在这里错了(。UI 线程是否并行执行时钟周期和已完成事件?

有人可以详细向我解释 UI 线程如何按顺序执行事件,例如示例的故事板已完成事件和调度程序计时器的时钟周期?感谢您的阅读,非常感谢您的评论,我正在努力解决这个问题。以下是我使用的测试代码,运行一段时间后最终会抛出错误。

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        storyBoardTest = new Storyboard();
        storyBoardTest.Completed += new EventHandler(storyBoardTest_Completed);
        DoubleAnimation animation = new DoubleAnimation(1, 0.9, new Duration(TimeSpan.FromSeconds(1)));
        Storyboard.SetTarget(animation, this);
        Storyboard.SetTargetProperty(animation, new PropertyPath(UIElement.OpacityProperty));
        storyBoardTest.Children.Add(animation);
        DispatcherTimer dt = new DispatcherTimer();
        dt.Interval = TimeSpan.FromMilliseconds(500);
        dt.Tick += new EventHandler(dt_Tick);
        dt.Start();
    }
    private Window windowTest = null;
    private Storyboard storyBoardTest = null;
    void dt_Tick(object sender, EventArgs e)
    {
        firstStep();
        secondStep();
    }
    private void firstStep()
    {
        windowTest = null;
    }
    private void secondStep()
    {
        windowTest = this;
        storyBoardTest.Stop();
        storyBoardTest.Begin(this);
    }
    void storyBoardTest_Completed(object sender, EventArgs e)
    {
        //Attempt to access object throws null error. Why?
        windowTest.Title = "test";
        windowTest = null;
    }
}

调用堆栈:

WPF应用程序1.exe!WpfApplication1.Window1.storyBoardTest_Completed(对象发送器 = {System.Windows.Media.Animation.ClockGroup}, System.EventArgs e = null( 第 63 行 C# 演示核心.dll!System.Windows.Media.Animation.Clock.FireEvent(System.Windows.EventPrivateKey key( + 0x5b 字节
演示核心.dll!System.Windows.Media.Animation.Clock.RaiseAccumulatedEvents(( + 0x160 字节
演示核心.dll!System.Windows.Media.Animation.TimeManager.RaiseEnqueuedEvents(( + 0x60 字节
演示核心.dll!System.Windows.Media.Animation.TimeManager.Tick(( + 0x28a 字节
演示核心.dll!System.Windows.Media.MediaContext.RenderMessageHandlerCore(object ressizeCompositionTarget( + 0xbc 字节
演示核心.dll!System.Windows.Media.MediaContext.AnimatedRenderMessageHandler(object resizeCompositionTarget( + 0x9d 字节

如果中间发生另一个事件,调度程序计时器如何在tick事件中执行其工作?这段代码有什么问题

每 500 毫秒启动一个运行一秒钟的故事板。这将不可避免地导致连续两个Completed事件没有中间Tick事件。

因此,您必须检查windowTest是否已在Completed处理程序中null

void storyBoardTest_Completed(object sender, EventArgs e)
{
    if (windowTest != null)
    {
        windowTest.Title = "test";
        windowTest = null;
    }
}

即使情节提要运行的时间少于 500 毫秒,也会有问题。由于Storyboard.Completed事件以与DispatcherTimer.Tick事件相同的方式追加到调度程序队列,并且调度程序计时器和情节提要的计时都不精确,因此两个事件处理程序的执行顺序不可靠。因此,在没有中间Tick事件的情况下,可能会发生两个Completed事件。


您可以添加一些跟踪输出,以查看两个处理程序是否在同一线程中运行。

void dt_Tick(object sender, EventArgs e)
{
    Trace.TraceInformation("Tick: {0}", Thread.CurrentThread.ManagedThreadId);
    ...
}
void storyBoardTest_Completed(object sender, EventArgs e)
{
    Trace.TraceInformation("Completed: {0}", Thread.CurrentThread.ManagedThreadId);
    ...
}