防止这种不安全的交叉线程

时间:2012-08-21 09:31:41

标签: c# multithreading

我有这段代码:

 private void Form1_Load(object sender, EventArgs e)
        {
            /*start update timer*/
            System.Timers.Timer updateticker = new System.Timers.Timer();
            updateticker.Elapsed += new ElapsedEventHandler(update_overload);
            //10 minute ticker
            updateticker.Interval = 600000;
            //30 sec ticker
            updateticker.Interval = 30000;
            updateticker.Enabled = true;

            System.Timers.Timer guiTimer = new System.Timers.Timer();
            guiTimer.Elapsed += new ElapsedEventHandler(idle_display);
            //1 minute ticker
            guiTimer.Interval = 60000;
            //30 sec ticker
            //updateticker.Interval = 30000;
            guiTimer.Enabled = true;

        }

 //run front end idle timer
        public void idle_display(object source, ElapsedEventArgs e)
        {
            if (minutes_left > 0) {
                minutes_left = minutes_left - 1;
            }

            lbl_dyn_status.Text = "Time until next automatic update: "+ minutes_left + " minutes.";
        }

Visual Studio将第二个函数的最后一行标记为不安全的交叉线程。任何人都可以建议我如何重写这个以解决问题?

干杯

4 个答案:

答案 0 :(得分:4)

使用System.Windows.Forms.Timer代替System.Timers.Timer

或使用Timer的SynchronizingObject属性。

  

当SynchronizingObject为null时,将在系统线程池的线程上调用处理Elapsed事件的方法。有关系统线程池的更多信息,请参阅ThreadPool。

     

当Elapsed事件由可视化Windows窗体组件(如按钮)处理时,通过系统线程池访问组件可能会导致异常或者可能无法正常工作。通过将SynchronizingObject设置为Windows窗体组件来避免此影响,这会导致处理Elapsed事件的方法在创建组件的同一线程上调用。

答案 1 :(得分:2)

正如其他人所建议的那样,使用System.Windows.Forms.Timer。与在线程池线程上运行的System.Threading.Timer不同,它保证在UI线程上调用tick事件。

UI线程是唯一允许修改UI的线程。这就是您尝试编写lbl_dyn_status.Text时遇到异常的原因。

答案 2 :(得分:1)

使用System.Windows.Forms.Timer计时器而不是System.Timers.Timer。

答案 3 :(得分:0)

将处理程序更改为

public void IdleDisplay(object source, ElapsedEventArgs e)
{
    if (lbl_dyn_status.InvokeRequired)
    {
        this.Invoke(IdleDisplay)
    }
    else
    {
        if (minutes_left > 0)
        {
            minutes_left = minutes_left - 1;
        }

        lbl_dyn_status.Text = string.Format(
            "Time until next automatic update: {0} minutes.",
             minutes_left);
    }
}

这种方式允许您使用无线程System.Threading.Timer,但检查处理程序中的跨线程调用。如果检测到,则通过Form类在主GUI线程上调用该调用。

这是on MSDN in more detail here.

所描述的

更一般地说,你不应该使用计时器来计算这样的时间。您的计时器绑定的线程越多,它就越可能与实际经过的时间不同。您可以使用计时器来安排时钟更新,但是,您应该计算自某个固定点以来的经过时间,而不是使用迭代计数器。