使用委托进行跨线程处理的 C# 问题
Posted
技术标签:
【中文标题】使用委托进行跨线程处理的 C# 问题【英文标题】:C# trouble using delegates for cross threading 【发布时间】:2016-05-07 06:42:04 【问题描述】:我在使用委托从不是主窗体线程的线程中更改 Textbox
时遇到问题。
我有两个类,一个带有 UI 的主 Form1.cs 类和另一个类 LINClass.cs,我在其中编写了一个设备函数。 在 Form1 中,我启动了一个不断轮询设备的后台工作程序,以及另一个从设备中检索数据的线程(RXTask()),这两个线程的所有功能都来自 LINCLass.cs。
从设备检索数据的线程包含一个委托,该委托指向允许更改 Form1 文本框的 Form1.cs 函数:
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 未被管理,在创建窗口句柄之前不能在控件上调用 Invoke 或 BeginInvoke”
【问题讨论】:
这是一个众所周知的问题。您需要谷歌 BeginInvoke 以获得规范的解决方案。基本上,您必须检查是否需要调用,如果是,则使用相同的参数调用它并停止在错误的线程上执行。 您可能在表单的Initialize()
方法完成运行之前调用了该方法。如果文本框尚未添加到 from 的控件集合中,您将收到此错误。
Windows 不是实时操作系统;您确定要尝试连续轮询设备吗?您是否愿意加热整个 CPU 以除此之外什么都不做?我的笔记本电脑的电池寿命不会感谢你。为什么不在一个线程上做所有事情并异步处理设备,而不是同时处理?
谢谢大家的建议,此时我在执行调用的代码之前用“蛮力”this.CreateHandle() 解决了这个问题,在这段代码之后用this.DestroyHandle() 解决了每个循环GUI 的句柄被创建和删除...
【参考方案1】:
您应该使用命令模式并将命令类从一个线程放入队列中,并让另一个线程从中读取。
【讨论】:
感谢您的回复,我是面向对象代码的新手,但我会研究您的建议! 您创建了一个基类和许多覆盖执行方法的子类。每个子类都有不同的执行方法。当您想要执行某个方法时,您可以创建一个特定对象并将其放入队列中。另一个线程逐个元素地读取队列并运行执行方法 我有一个疑问,所以我想问你另一个问题:我用一个名为 LINClass (RXThread = new Thread(new ThreadStart( LIN.RXTask));),RXThread可以访问实例的所有成员吗?或者换句话说,RXThread 正在处理实例 LIN? 应该可以,如果我理解正确的话。您应该始终小心线程锁,并且应该花几天时间来研究它。【参考方案2】:您需要防止调用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
。)
【讨论】:
这是一个非常不靠谱的建议,OnHandleCreated() 可以运行不止一次。当他遵循您的建议时,OP 很容易启动线程,那将非常糟糕。建议改为使用 Load 事件。 @HansPassant 我不知道可以多次调用 OnHandleCreated()。这是在哪里记录的?我在任何地方都找不到它(例如在documentation like this)。 自己尝试一下,例如更改 ShowInTaskbar 属性。我已经在很多帖子中记录了这一点:) @HansPassant 嗯,微软似乎有piled quite a lot of code intoControl.OnHandleCreated()
...
@MatthewWatson 一般的答案是:每当调用Control.RecreateHandle()
时,都会额外调用该函数。其中一种情况是changing ShowInTaskbar
以上是关于使用委托进行跨线程处理的 C# 问题的主要内容,如果未能解决你的问题,请参考以下文章