【问题标题】:Calling Thread must be STA调用线程必须是 STA
【发布时间】:2014-11-29 11:05:41
【问题描述】:

我正在尝试模拟弹丸并尝试在定时事件中只创建一次标签,因为它在参数中有一个对象,我需要在定时事件中。但是当我运行它时,我得到了这个线程错误。请帮忙!

我的代码是:

    void onTimedEvent(Object source, ElapsedEventArgs e)
    {
        particle newProjectile;
        newProjectile = new particle();

        bool LabelExist = false;
        if (LabelExist == false)
        {
            CreateLabels(newProjectile);
        }
    }

        aTimer = new System.Timers.Timer(200);
        aTimer.Elapsed += onTimedEvent;
        aTimer.Enabled = true;

    void CreateLabels(particle newProjectile)
    {
        DockPanel panelcontrol_displacement, panelcontrol_CurrentVel, panelcontrol_VerticalVel, panelcontrol_HorizontalVel, panelcontrol_Time;
        Label lbl_TimeUnits, lbl_TimeOutput, lbl_Time, lbl_DisplacementOutput, lbl_DisplacementUnits, lbl_Displacement, lbl_CurrentVel, lbl_CurrentVelOutput, lbl_HorizontalVelocityOutputs, lbl_HorizontalVelocityUnits, lbl_VerticalVelocity, lbl_HorizontalVelocity, lbl_CurrentVelUnits, lbl_VerticalVelocityOutput, lbl_VerticalVelocityUnits;
        lbl_Time = new Label();
        lbl_Time.Content = "Time";
        lbl_Time.Height = 30;
        lbl_Time.Width = 110;
        lbl_TimeOutput = new Label();
        lbl_TimeOutput.Content = "20";
        lbl_TimeOutput.Height = 30;
        lbl_TimeOutput.Width = 100;
        lbl_TimeOutput.HorizontalContentAlignment = HorizontalAlignment.Right;
        lbl_TimeUnits = new Label();
        lbl_TimeUnits.Content = "s";
        lbl_CurrentVel = new Label();
        lbl_CurrentVel.Content = "Current Velocity";
        lbl_CurrentVel.Height = 30;
        lbl_CurrentVel.Width = 110;
        lbl_CurrentVelOutput = new Label();
        lbl_CurrentVelOutput.Content = "20";
        lbl_CurrentVelOutput.Height = 30;
        lbl_CurrentVelOutput.Width = 100;
        lbl_CurrentVelOutput.HorizontalContentAlignment = HorizontalAlignment.Right;
        lbl_CurrentVelUnits = new Label();
        lbl_CurrentVelUnits.Content = "m/s";
        lbl_VerticalVelocity = new Label();
        lbl_VerticalVelocity.Content = "Verticle Velocity";
        lbl_VerticalVelocity.Height = 30;
        lbl_VerticalVelocity.Width = 110;
        lbl_VerticalVelocityOutput = new Label();
        lbl_VerticalVelocityOutput.Content = "20";
        lbl_VerticalVelocityOutput.Height = 30;
        lbl_VerticalVelocityOutput.Width = 100;
        lbl_VerticalVelocityOutput.HorizontalContentAlignment = HorizontalAlignment.Right;
        lbl_VerticalVelocityUnits = new Label();
        lbl_VerticalVelocityUnits.Content = "m/s";
        lbl_HorizontalVelocity = new Label();
        lbl_HorizontalVelocity.Content = "Horizontal Velocity";
        lbl_HorizontalVelocity.Height = 30;
        lbl_HorizontalVelocity.Width = 110;
        lbl_HorizontalVelocityOutputs = new Label();
        lbl_HorizontalVelocityOutputs.Content = "20";
        lbl_HorizontalVelocityOutputs.Height = 30;
        lbl_HorizontalVelocityOutputs.Width = 100;
        lbl_HorizontalVelocityOutputs.HorizontalContentAlignment = HorizontalAlignment.Right;
        lbl_HorizontalVelocityUnits = new Label();
        lbl_HorizontalVelocityUnits.Content = "m/s";
        lbl_Displacement = new Label();
        lbl_Displacement.Content = "Displacement";
        lbl_Displacement.Height = 30;
        lbl_Displacement.Width = 110;
        lbl_DisplacementOutput = new Label();
        lbl_DisplacementOutput.Content = "20";
        lbl_DisplacementOutput.Height = 30;
        lbl_DisplacementOutput.Width = 100;
        lbl_DisplacementOutput.HorizontalContentAlignment = HorizontalAlignment.Right;
        lbl_DisplacementUnits = new Label();
        lbl_DisplacementUnits.Content = "m/s";
        panelcontrol_HorizontalVel = new DockPanel();
        panelcontrol_CurrentVel = new DockPanel();
        panelcontrol_VerticalVel = new DockPanel();
        panelcontrol_displacement = new DockPanel();
        panelcontrol_Time = new DockPanel();
        panelcontrol_CurrentVel.Children.Add(lbl_CurrentVel);
        panelcontrol_CurrentVel.Children.Add(lbl_CurrentVelOutput);
        panelcontrol_CurrentVel.Children.Add(lbl_CurrentVelUnits);
        panelcontrol_VerticalVel.Children.Add(lbl_VerticalVelocity);
        panelcontrol_VerticalVel.Children.Add(lbl_VerticalVelocityOutput);
        panelcontrol_VerticalVel.Children.Add(lbl_VerticalVelocityUnits);
        panelcontrol_HorizontalVel.Children.Add(lbl_HorizontalVelocity);
        panelcontrol_HorizontalVel.Children.Add(lbl_HorizontalVelocityOutputs);
        panelcontrol_HorizontalVel.Children.Add(lbl_HorizontalVelocityUnits);
        panelcontrol_displacement.Children.Add(lbl_Displacement);
        panelcontrol_displacement.Children.Add(lbl_DisplacementOutput);
        panelcontrol_displacement.Children.Add(lbl_DisplacementUnits);
        panelcontrol_Time.Children.Add(lbl_Time);
        panelcontrol_Time.Children.Add(lbl_TimeOutput);
        panelcontrol_Time.Children.Add(lbl_TimeUnits);
        StkPnl_Inputs.Children.Add(panelcontrol_Time);
        StkPnl_Inputs.Children.Add(panelcontrol_CurrentVel);
        StkPnl_Inputs.Children.Add(panelcontrol_VerticalVel);
        StkPnl_Inputs.Children.Add(panelcontrol_HorizontalVel);
        StkPnl_Inputs.Children.Add(panelcontrol_displacement);
        //databinding
        Binding(newProjectile, lbl_CurrentVelOutput, "CurrentVelocity");
    }

【问题讨论】:

  • 99.9% 的代码是不相关的。缺少的是关于定时器类型等的信息。它必须从ElapsedEventArgs推导出来。
  • aTimer = new System.Timers.Timer(200); aTimer.Elapsed += onTimedEvent; aTimer.Enabled = true; aTimer.Elapsed += onTimedEvent; aTimer.Enabled = true;
  • UI 代码应该在 UI(主)线程上执行。使用 DispatcherTimer。
  • 此外,您的计时器将在每次滴答时调用onTimedEvent 两次。如果这是故意的,那么这是一种晦涩(糟糕)的写法。
  • 使用 Dispatcher 计时器(如目前为止的一个答案所建议的那样)将使异常消失。但目前还不清楚是否会修复代码中的所有错误。你为什么要订阅定时器事件两次?为什么您认为每秒 重新创建 和重新初始化这些控件五次是有意义的?将控制值绑定到每 200 毫秒更新一次的数据结构不是更有意义吗?

标签: c# wpf multithreading timer


【解决方案1】:

您收到此错误是因为不允许从后台线程访问 UI(System.Timers.Timer.Elapsed 处理程序正在后台线程上运行)。这是因为在 WPF 中处理 UI 的所有线程都应该在 Single Threaded Apartment(STA) 中运行以进行同步。而且,后台工作人员不在 STA 中运行。

使用DispatcherTimer,而不是System.Timers.Timer。

MSDN 页面的备注部分概述了这两个计时器之间的区别。

如果在 WPF 应用程序中使用 System.Timers.Timer,则值得注意的是,System.Timers.Timer 运行在与用户界面 (UI) 线程不同的线程上。为了访问用户界面 (UI) 线程上的对象,需要使用 Invoke 或 BeginInvoke 将操作发布到用户界面 (UI) 线程的 Dispatcher。

使用 DispatcherTimer 而不是 System.Timers.Timer 的原因是 DispatcherTimer 与 Dispatcher 在同一线程上运行,并且可以在 DispatcherTimer 上设置 DispatcherPriority。

【讨论】:

  • 如果您添加此错误出现的原因,它可能会对其他人有所帮助。
  • 编辑问题后: CreateLabels 是 Timer 所做的一切。使用 Timers.Timer 然后将所有工作编组回来是无稽之谈。
  • @HenkHolterman 认为有充分的理由使用 System.Timers.Timer,现在很明显从一开始就选择了错误的计时器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多