连接事件期间C# - “跨线程操作无效”错误

时间:2013-07-06 19:19:46

标签: c# multithreading winforms events thread-safety

我为WinForm应用程序编写了一些代码,通过更改颜色编码的标签控件来显示正在运行的计算机的连接状态。

在我的表格中,我有以下代码:

public frmShell()
{
    InitializeComponent();
    this.stateManager = new StateManager();
}

private void frmShell_Load(object sender, EventArgs e)
{
    // Subscribe to events
    this.stateManager.ConnectionChange += new StateManager.ConnectionChangeHandler(ConnectionHasChanged);
}

private void ConnectionHasChanged(object sender, ConnectionChangeEventArgs e)
{
    if (e.ConnectionType == ConnectionType.Network)
    {
        if (e.ConnectionState == ConnectionState.Connected)
        {
            SetLabelOnline();
        }
        else
        {
            SetLabelOffline();
        }
    }
}
private void SetLabelOffline()
{
    labelConnectivityValue.Text = "Offline";
    labelConnectivityValue.ForeColor = Color.Red;
}

private void SetLabelOnline()
{
    labelConnectivityValue.Text = "Online";
    labelConnectivityValue.ForeColor = Color.Green;
}

每次我禁用网络适配器来测试我的代码时,我都会在 SetLabelOnline() SetLabelOffline()方法中收到followig错误:

  

跨线程操作无效:控制'labelConnectivityValue'从其创建的线程以外的线程访问。

1。我无法理解为什么我的代码被视为无效的跨线程操作。此外,我只是以相同的方式重新使用我之前在另一个WinForm应用程序中使用的代码。

2。我不知道如何解决这个问题,特别是考虑到我想要实现的目标看起来非常基本。

注意:statemanager中的代码只是一个定时器,它会经常检查连接状态,并在必须更改连接属性时触发事件,即连接状态是否已更改

2 个答案:

答案 0 :(得分:3)

触发该事件的计时器或其他东西是从不同于拥有表单的程序(及其所有控件)执行的。

这可能在早期版本的WinForms中“有效”,因为现在抛出异常的显式检查不存在。然而,它无法正常 ,这就是添加这些检查的原因,以帮助人们发现他们遇到问题。

有两种方法可以解决这个问题:

  1. 确保调用该事件的代码未使用辅助线程。你提到了一个计时器,可能是这个代码错误地使用了错误的计时器类型。你应该研究一下。

    如果代码使用的是System.Threading.Timer,请验证它是否可以使用System.Windows.Forms.Timer。第二个计时器使用构建Winforms系统的消息传递系统,它将在拥有表单及其控件的同一线程上调用事件。

  2. 确保事件中的代码可以应对在不同的线程上执行。
  3. 执行此操作的正确方法(即上面的第2点)是将每个调用封送到拥有表单及其控件的同一个线程,您可以通过更改每个此类方法的标头来执行此操作,如下所示:

    private void ConnectionHasChanged(object sender, ConnectionChangeEventArgs e)
    {
        if (InvokeRequired)
        {
            Invoke(new Action(() =>
            {
                ConnectionHasChanged(sender, e);
            }));
            return;
        }
    
        if (e.ConnectionType == ConnectionType.Network)
        {
            ...
    

答案 1 :(得分:2)

我认为你问题的解决方案在于你写的最后一段。 你说你在StateManager中使用了一个Timer。

.NET框架中有三种常见的计时器:

  1. System.Timers.Timer(http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx
  2. System.Threading.Timer(http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx
  3. System.Windows.Forms.Timer(http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx
  4. 前两个计时器正在使用Threads来完成他们的工作并且不是线程安全的。最后一个,Forms.Timer是在处理WinForms时使用计时器的首选方式。

    您有两种选择:

    1. 确保StateManager计时器是WinForm计时器。
    2. 使用InvokeRequired方法:http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired.aspx了解您是否在正确的主题上。