我的PictureBox调用上有另外一个question给我3种错误,特别是来自Conrad Frix的一些很好的答案。所以它让我弄清楚我的问题在哪里,但现在要修复它我不是百分百肯定的。
基本上我有一个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中的线程计时器调用我这样做导致了这个....
答案 0 :(得分:1)
顾名思义,InvokeRequired
表示您在访问控件时需要致电Invoke
(或BeginInvoke
)。
请注意,这是Control.Invoke
/ Control.BeginInvoke
,不代理中存在的Invoke/BeginInvoke
...虽然您需要一个代理人调用 Invoke
/ BeginInvoke
,只是为混合添加更多混淆。
有关详细信息,请参阅Windows Forms section of my threading tutorial。整个教程可以做更新,但我相信这一点是可以的。在其他情况下,您可能还需要考虑使用BackgroundWorker
,但我不认为在这种特殊情况下这可能与您相关。
答案 1 :(得分:1)
我认为您对InvokeRequired的理解有误。 InvokeRequired表示当前线程与UI线程不同,现在访问控制状态是不安全的。如果是这种情况,则必须使用Control.Invoke
封送对UI线程的调用,然后访问控制状态。请阅读here on MSDN了解详情。
在您的情况下,除非PictureBox图像正在发生变化,否则我建议您先预先复制图像并使用它。否则,您需要使用Control.Invoke
。
答案 2 :(得分:1)
你有太多的线程要把它带到一个好的结局。 Timer和委托的BeginInvoke()方法都将使用线程池线程。问题是PictureBox.Image属性只是部分线程安全的。它一次只能由一个线程访问。当您的代码调用Clone()方法的同时,UI线程绘制图像时,您的代码将会出现异常。
您的锁定语句无法解决问题,PictureBox正在访问Image属性而不使用相同的锁定。我强烈建议首先删除线程,使用System.Windows.Forms.Timer而不是System.Threading.Timer。在UI线程上引发了Tick事件。然而,这将使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);
}
}
}