2016-01-29 13 views
4

我有一个问题,使用委托从不是主窗体线程的线程更改Textbox使用代表进行交叉线程的C#麻烦

我有两个类,一个主要的Form1.cs类与UI和另一个类LINClass.cs,我写了一个设备函数。 在Form1中,我启动了一个连续轮询设备的背景工作器,另一个线程从设备检索数据(RXTask()),两个线程的所有功能都来自LINCLass.cs。

从设备中检索数据的线程包含一个代表指向某Form1.cs的函数,其允许改变Form1中的文本框:

public class LINClass : Form 
{ 
    private delegate void FormUpdater(int devnum, string rpm, string current, string temp); 

//some other variables and procedure 

public void RXTask() 
{ 
    FormUpdater frmUpdt = new FormUpdater(Form1.GUIupdate); 
    //other procedures and a loop containing the invoke... 
    this.Invoke(frmUpdt, new object[]{devnum, rpm, 
             current, 
             temperature}); 

} 

Form1类包含调用的方法,写成下面

public static void GUIupdate(int eWPnum, string rpm, string current, string temp) 
{ 
    //take the parameters and write them in the textbox 
} 

现在,当我运行代码时,线程正在运行,但在调用函数时我有一个异常。

http://s13.postimg.org/9ohuj9d7r/exception.png

它说,“InvalidOperationException异常不管理,调用或BeginInvoke可直到窗口句柄已创建不能在控制称为”

+0

这是一个众所周知的问题。你需要谷歌BeginInvoke的规范解决方案。基本上,你必须检查是否需要调用,如果是你使用相同的参数调用它,并停止在错误的线程上执行。 – Carlos

+0

您可能在窗体的'Initialize()'方法运行完成之前调用该方法。如果文本框尚未添加到from的控件集合中,则会出现此错误。 – InBetween

+1

Windows不是实时操作系统;你确定你想尝试轮询设备**连续**吗?你是否愿意加热整个CPU,除此之外什么都不做?我的笔记本电脑的电池寿命不会感谢你。为什么不在一个线程上做所有事情,并且不是同时处理设备? –

回答

1

你应该使用一个命令模式,并把命令类从一个线程进入队列,并让其他线程读取它。

+0

谢谢你的回复,我很新的面向对象的代码,但我会研究你的建议! –

+0

你创建了一个基类和很多覆盖execute方法的子类,每个子类都有一个不同的执行方法。当你想要执行一个方法时,你需要创建一个特定的对象并将其放入队列中。另一个线程读取队列,逐个元素并运行执行方法 – user853710

+0

我有一个疑问,所以我想问你另一个问题:我启动了一个线程,该方法位于类LINClass的实例LIN内(RXThread = new Thread(new ThreadStart(LIN.RXTask));),RXThread可以访问实例的所有成员?换句话说,RXThread正在处理实例LIN?如果我正确理解你的话, –

1

除非窗体窗口已创建,否则您需要阻止this.Invoke()被调用。

要做到这一点,最简单的方法是重写OnLoad()并设置一个标志:

private bool isLoaded; 

protected override void OnLoad(EventArgs e) 
{ 
    base.OnLoad(e); 
    Volatile.Write(ref isLoaded, true); 
} 

然后调用前检查标志:

public void RXTask() 
{ 
    FormUpdater frmUpdt = new FormUpdater(Form1.GUIupdate); 
    //other procedures and a loop containing the invoke... 

    if (Volatile.Read(ref isLoaded)) 
    { 
     this.Invoke(frmUpdt, new object[] 
     { 
      devnum, rpm, 
      current, 
      temperature 
     }); 
    } 
} 

(如果你的.NET版本没有按”没有Volatile.Read()/ Volatile.Write(),取而代之的是将该标志声明为volatile)。

+1

这是非常可靠的建议,OnHandleCreated()可以运行多次。当他遵从你的建议时,OP很容易启动线程,这将是非常糟糕的。不要推荐Load事件。 –

+0

@HansPassant我不知道OnHandleCreated()可以被多次调用。这在哪里记录?我无法在任何地方找到它(例如[像这样的文档](https://msdn.microsoft.com/en-us/library/86faxx0d.aspx))。 –

+0

只需亲自尝试,例如更改ShowInTaskbar属性。我已经在很多文章中记录了这个:) –