通常,当您访问线程中的控件时,最终会出现一些跨线程异常 . 在我的C#WinForms应用程序中,我有一个图片框和一个工具条标签,它们不会导致异常 . 我不明白为什么,有人可以向我解释一下吗?
这里有一些代码说明:
在主要形式中,我有一个picturebox和一个toolstriplabel . 此外,我还引用了另一个表单,该表单没有控件,也没有其他源代码 . 然后在主窗体中有另一个与线程一起工作的对象 . 该线程可以引发三个不同的事件,主表单订阅这三个事件 .
-
Event1导致toolstriplabel更新(带有来自线程的一些信息) .
-
Event2导致图片框更新(使用线程中的新图片) .
Event1和Event2工作得非常好 . 我没有使用任何invoke方法,我直接更改Text和BackgroundImage属性而没有跨线程异常 .
- Event3虽然有麻烦 . 它应该显示另一种形式,但我收到了交叉的therad例外 . 它只有在我使用BeginInvoke来显示表单时才有效 .
这是为什么?
Edit:
多线程由MJPEGStream对象完成 . 我订阅了该MJPEGStream对象的NewFrame方法 .
public partial class Form1 : Form
{
private CAM cam;
private PeekWindow frmPeekWindow;
public Form1()
{
InitializeComponent();
cam = new CAM();
cam.NewImageMessageEvent += new NewImageEventHandler(cam_NewImageMessageEvent);
cam.DetectionEvent += new DetectionEventHandler(cam_DetectionEvent);
cam.FpsChangedMessageEvent += new FpsChangedEventHandler(cam_FpsChangedMessageEvent);
cam.DetectionThreshold = (float)this.numDetectionThreshold.Value;
frmPeekWindow = new PeekWindow();
// without the next two lines, frmPeekwindow.Show() won't work if called in an event
frmPeekWindow.Show();
frmPeekWindow.Hide();
}
void cam_FpsChangedMessageEvent(object sender, FpsChangedEventArgs e)
{
lblFPS.Text = string.Format("fps: {0:0.0}", e.FPS);
}
void cam_DetectionEvent(object sender, DetectionEventArgs e)
{
if (chkEnablePeakWindow.Checked)
{
if (frmPeekWindow.InvokeRequired)
{
frmPeekWindow.Invoke((MethodInvoker)delegate()
{
frmPeekWindow.Show();
frmPeekWindow.setImage(e.Image);
});
}
else
{
frmPeekWindow.Show();
frmPeekWindow.setImage(e.Image);
}
}
}
void cam_NewImageMessageEvent(object sender, NewImageEventArgs e)
{
picStream.BackgroundImage = e.Image;
}
}
这是CAM类:
class CAM
{
private object lockScale = new object();
private MJPEGStream stream;
private Bitmap image;
public event NewImageEventHandler NewImageMessageEvent;
public event FpsChangedEventHandler FpsChangedMessageEvent;
public event DetectionEventHandler DetectionEvent;
// configure (login, pwd, source)
public CAM()
{
this.stream = new MJPEGStream("...");
this.stream.Login = "...";
this.stream.Password = "...";
this.stream.NewFrame += new NewFrameEventHandler(OnNewFrame)
}
private void OnNewFrame(object sender, NewFrameEventArgs ev)
{
try
{
FpsChangedMessageEvent(this, new FpsChangedEventArgs(10));
// get image
image = ev.Frame;
NewImageMessageEvent(this, new NewImageEventArgs(new Bitmap(image)));
DetectionEvent(this, new DetectionEventArgs(new Bitmap(image)));
}
catch (Exception ex)
{
Console.Out.WriteLine(ex.Message);
}
}
}
3 回答
您不会获得跨线程异常,但这并不意味着这是一个安全的操作 . 您的控制总是有可能变得不稳定 . 你只是不知道什么时候会发生 .
请参阅Microsoft的以下说明 . http://msdn.microsoft.com/en-us/library/ms171728.aspx
我有三个可能性:
该动作已经发送到gui线程 .
当前不需要调度该操作 .
动作以某种方式从gui线程执行 .
这很可能是3号 .
您不一定总是必须调用BeginInvoke / Invoke . 有时操作在前台线程上运行,有时它在后台运行 .
根据无处不在的微软样本,您可以检查是否需要调用BeginInvoke / Invoke .
这是一篇很好的微软文章,有一个样本:http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
这是另一篇关于如何"avoid"模式的文章:http://www.codeproject.com/Articles/37642/Avoiding-InvokeRequired