在 PictureBox 上 InvokeRequired 为真。如何处理?
Posted
技术标签:
【中文标题】在 PictureBox 上 InvokeRequired 为真。如何处理?【英文标题】:InvokeRequired is true on PictureBox. How to deal with this? 【发布时间】:2010-09-13 06:00:17 【问题描述】:我的 PictureBox 电话中有另一个 question 给我 3 种错误,特别是来自 Conrad Frix 的一些很好的答案。所以它让我弄清楚我的问题出在哪里,但现在要解决它我不是 100% 确定的。
基本上我有一个 Windows 窗体计时器,它正在检查某个事件是否为真,如果是,它会告诉系统在所述事件(值)超过某个阈值后 2 秒发送一些数据。
我认为我拥有的所有计时器都在用我的 PictureBox 创建一个令人讨厌的竞争条件,我在几个地方使用它来获取图像:
new Bitmap(myPicBox.Image);
等等……
我在某处读到,计时器的间隔应该至少为 50。将其设置为 33。我发现我可以执行 picCapture.InvokeRequired 来查看它是否会基本上死掉。我知道我需要使用一个委托,但只使用它们来设置一些东西......而不是从中获取图像......不知道如何设置它......我知道确实是什么原因造成的,就是这个代码组合:
private void timer1_Tick(object sender, EventArgs e)
if(someCOnditionTrue)
TimerCallback tc = new TimerCallback(sendDataFast); //only
//doing all this so i can have the method run two seconds after
// the condition is detected to be true.
System.Threading.Timer t = new System.Threading.Timer(tc, null, 2000, Timeout.Infinite);
void sendDataFast(Object stateObject)
//using this so the execution is not haulted while the sending of data takes place.
EmergencyDelegate delEmergency =
new EmergencyDelegate(mic.sendEmergencyData);
Image imgclone;
if (picCapture.InvokeRequired)
Console.WriteLine("HFS Batman! its going to die ");
lock (lockObject2) //i admit no clue what im doing here and doesn't seem to help.
Image img = picCapture.Image;
imgclone = (Image)img.Clone();
delEmergency.BeginInvoke(imgclone, null, null); //deep in the call to
//sendEmergencyData i get the **ParameterNotValid** almost everytime.
imgclone.Dispose(); //to free memory?
根据我之前的问题,在 timer1_tick 事件中似乎不再出现内存问题或其他错误...(内存不足错误是一个)。
我认为最大的问题是当我需要它的图像数据时如何处理 picCapture.InvokeRequired?我确定它在 timer1_click 中的线程定时器调用导致了这个....
【问题讨论】:
【参考方案1】:顾名思义,InvokeRequired
表示访问控件时需要调用Invoke
(或BeginInvoke
)。
请注意,这是Control.Invoke
/Control.BeginInvoke
,不是代表中存在的Invoke/BeginInvoke
...尽管您需要一个代表才能调用 Invoke
/BeginInvoke
,只是为了增加混乱。
有关详细信息,请参阅Windows Forms section of my threading tutorial。整个教程可以做更新,但我相信这一点没问题。在其他情况下,您可能还想考虑使用BackgroundWorker
,但我认为在这种特殊情况下这可能与您无关。
【讨论】:
谢谢我马上检查一下。我之前使用过委托来调用一个方法,但那是我的“真正的工作”,我这里没有代码,所以不确定我在那里做了什么......有一段时间了。 @Jon:关于yoda.arachsys.com/csharp/threads/shutdown.shtml 的例子,真的有必要在“停止”变量上使用锁吗?似乎它只能通过 Stop() 调用设置,并且永远不能取消设置。只是问,因为我用写了一个非常相似的类,但我没有锁定我等价的停止变量。 @SomethingBetter:您要么需要它是易失的,要么需要锁定。否则不能保证一个线程中的集合会在另一个线程中看到。【参考方案2】:我认为您对 InvokeRequired 的理解有误。 InvokeRequired 表示当前线程与UI线程不同,现在访问控件状态是不安全的。如果是这种情况,那么您必须使用Control.Invoke
来编组对 UI 线程的调用,然后访问控制状态。阅读here on MSDN了解更多信息。
在您的情况下,除非 PictureBox 图像发生变化,否则我建议您先复制图像并使用它。否则需要使用Control.Invoke
。
【讨论】:
【参考方案3】:你有太多的线程来结束这件事。 Timer 和委托的 BeginInvoke() 方法都将使用线程池线程。问题是 PictureBox.Image 属性只是部分线程安全的。它一次只能被一个线程访问。当 UI 线程在您的代码调用 Clone() 方法的同时绘制图像时,您的代码将异常终止。
您的 lock 语句没有解决问题,PictureBox 正在访问 Image 属性而不使用相同的锁。我强烈建议首先摆脱线程,使用 System.Windows.Forms.Timer 而不是 System.Threading.Timer。它的 Tick 事件在 UI 线程上引发。然而,这将使 UI 线程在事件运行时无响应,这取决于用户是否注意到这需要多长时间。例如,超过 100 毫秒就会成为问题。
唯一的其他方法是尝试使 PictureBox 控件线程安全。这在某种程度上是可能的。向您的项目添加一个新类并粘贴如下所示的代码。编译。将新控件从工具箱顶部拖放到表单上,替换现有的 PB。请注意,这只是部分解决方案,显示动画 GIF 或使用 ImageLocation 属性仍然会爆炸。使用提供的 Clone 方法,而不是在 Image 属性上调用 Clone。
using System;
using System.Drawing;
using System.Windows.Forms;
class MyPictureBox : PictureBox
private object locker = new object();
public new Image Image
get return base.Image;
set lock (locker) base.Image = value;
public Image Clone()
lock (locker)
return (this.Image != null) ? (Image)this.Image.Clone() : null;
protected override void OnPaint(PaintEventArgs pe)
lock (locker)
base.OnPaint(pe);
【讨论】:
好的,我喜欢它的外观,让我感觉更加温暖和模糊。我看到线程太多的问题,我无法解决这个问题。我正在使用的计时器(至少是主要的)是一个窗口形式的计时器。其他的是线程计时器和委托。我正在尝试做的事情听起来很容易,但显然幕后发生的事情比想象的要多得多。我想我可以在我的表单上使用这个新控件,并且仍然可以通过该 Image 属性访问图像? 是的,这就是目的。试试看。以上是关于在 PictureBox 上 InvokeRequired 为真。如何处理?的主要内容,如果未能解决你的问题,请参考以下文章
在 PictureBox 上 InvokeRequired 为真。如何处理?
在 XP 上运行时 Winform PictureBox 不透明
C# 在带有 Graphics.DrawLine() 的 PictureBox 上绘制不适用于 Paint 事件