基于线程的绘图中的 InvalidOperationException
InvalidOperationException in thread based drawing
我是编码游戏的新手,所以我不太确定如何解决这个问题。我的代码从一个线程中提取信息,该线程从一个 9DOF 传感器中获取数据,该传感器以三个欧拉角的形式出现。然后使用数据生成点,在屏幕上用绘图 class 从这些点创建一个圆圈。 To 完美地工作了一段时间,但最终总是会给出这个异常,说在 System.Drawing.dll 中发生了一个类型为 'System.InvalidOperationException' 的未处理的异常,并附加信息说该对象当前正在别处使用。在仔细研究并尝试绘制的内容后,我得出(可能不正确的)结论,即线程发送数据的速度快于主代码渲染绘图的速度。我怎样才能防止这种情况发生?下面是代码的摘录。
private void button3_Click(object sender, EventArgs e)
{
//single node test
Nodes.NodesList.Add(new RazorIMU("COM7"));
Nodes.NodesList[0].StartCollection('e');
Nodes.NodesList[0].CapturedData += new RazorDataCaptured(Fusion_CapturedData);
public void Fusion_CapturedData(float[] data, float deltaT)
{
int centerX = (int)(300 + (5 / 3) * data[0] + 0.5);
int centerY = (int)(300 + (5 / 3) * data[1] + 0.5);
int endPointX = (int)(centerX + 25 * Math.Sin(Math.PI / 180 * data[2]) + 0.5);
int endPointY = (int)(centerY + 25 * Math.Cos(Math.PI / 180 * data[2]) + 0.5);
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
/*g.DrawLine(new Pen(Color.Yellow), 300, 0, 300, 600);
g.DrawLine(new Pen(Color.Yellow), 0, 300, 600, 300);
g.DrawLine(new Pen(Color.LightYellow), 150, 0, 150, 600);
g.DrawLine(new Pen(Color.LightYellow), 0, 150, 600, 150);
g.DrawLine(new Pen(Color.LightYellow), 450, 0, 450, 600);
g.DrawLine(new Pen(Color.LightYellow), 0, 450, 600, 450);*/
g.DrawEllipse(new Pen(Color.Green), centerX - 25, centerY - 25, 50, 50);
g.DrawLine(new Pen(Color.Green), centerX, centerY, endPointX, endPointY);
}
pictureBox1.Image = bmp;
}
这是主要代码。线程只是以接收信息的速度发送信息,所以我认为我不需要把它放在这里,除非有人另有说明。
{
public 委托 void RazorDataCaptured(float[] 数据, float deltaT);
/// <summary>
///
/// Object for Sparkfun's 9 Degrees of Freedom - Razor IMU
/// Product ID SEN-10736
/// https://www.sparkfun.com/products/10736
///
/// Running Sample Firmware
/// https://github.com/a1ronzo/SparkFun-9DOF-Razor-IMU-Test-Firmware
///
/// </summary>
public class RazorIMU : IDisposable
{
private SerialPort _com;
private static byte[] TOGGLE_AUTORUN = new byte[] { 0x1A };
public string Port { get; private set; }
private Thread _updater;
private string[] _parts;
public event RazorDataCaptured CapturedData = delegate { };
private float[] _data = new float[9];
private static bool running = false;
/// <summary>
/// Create a new instance of a 9DOF Razor IMU
/// </summary>
/// <param name="portName">Serial port name. Ex: COM1.</param>
public RazorIMU(string portName)
{
// Create and open the port connection.
_com = new SerialPort(portName, 57600, Parity.None, 8, StopBits.One);
_com.Open();
Port = portName;
// Set the IMU to automatically collect data if it has not done yet.
//Thread.Sleep(3000);
//_com.Write("#s00");
//_com.DiscardInBuffer();
}
/// <summary>
/// Start continuous collection of data.
/// </summary>
public void StartCollection(char dataType)
{
running = true;
if (dataType == 'r') _updater = new Thread(new ThreadStart(ContinuousCollect));
else if (dataType == 'q') _updater = new Thread(new ThreadStart(ContinuousCollectQuat));
else if (dataType == 'e') _updater = new Thread(new ThreadStart(ContinuousCollectEuler));
_updater.Start();
}
/// <summary>
/// Stop continuous collect of data.
/// </summary>
public void StopCollection()
{
if (_updater != null)
{
running = false;
_updater.Join();
_updater = null;
}
}
/// <summary>
/// This method is extremely important. It continously updates the data array.
/// Data is read and the change in time since the last read is calculated.
/// The CapturedData event is triggered, sending the new data and the change in time.
/// </summary>
private void ContinuousCollect()
{
_com.WriteLine("#osr"); //Sets sensor output data to raw.
_com.ReadLine(); //Discards first line if broken.
while (running) //Static Boolean that controls whether or not to keep running.
{
ReadDataRaw();
CapturedData(_data, 0.020F);
}
}
private void ContinuousCollectQuat()
{
_com.WriteLine("#ot"); //Sets sensor output data to quaternions.
_com.ReadLine(); //Discards first line if broken.
while (running) //Static Boolean that controls whether or not to keep running.
{
ReadDataQuat();
CapturedData(_data, 0.020F);
}
}
private void ContinuousCollectEuler()
{
_com.WriteLine("#ob"); //Sets sensor output data to quaternions.
_com.ReadLine(); //Discards first line if broken.
while (running) //Static Boolean that controls whether or not to keep running.
{
ReadDataEuler();
CapturedData(_data, 0.020F);
}
}
/// <summary>
/// Get a single sample of the 9DOF Razor IMU data.
/// <para>Format: [accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z,mag_x,mag_y,mag_z]</para>
/// </summary>
/// <param name="result">double array of length 9 required.</param>
private void ReadDataRaw()
{
_parts = _com.ReadLine().Split(',');
if (_parts.Length == 9)
for (int i = 0; i < 9; i++)
_data[i] = float.Parse(_parts[i]);
}
private void ReadDataQuat()
{
_parts = _com.ReadLine().Split(',');
if (_parts.Length == 4)
for (int i = 0; i < 4; i++)
_data[i] = float.Parse(_parts[i]);
}
private void ReadDataEuler()
{
_parts = _com.ReadLine().Split(',');
if (_parts.Length == 3)
for (int i = 0; i < 3; i++)
_data[i] = float.Parse(_parts[i]);
}
/// <summary>
///
/// </summary>
public void Dispose()
{
StopCollection();
if (_com != null) //Make sure _com exists before closing it.
_com.Close();
_com = null;
_data = null;
_parts = null;
}
}
}
问题:
- 你必须dispose pens。
- 您正在创建一些由您创建的线程中的上升事件,因此所有事件处理程序在访问 UI 元素之前必须使用
Invoke
。
您可以在 Form
中使用这个简单的事件处理程序模板(将其应用于 Fusion_CapturedData()
):
public void SomeEventHandler(someparameters)
{
if (InvokeRequired)
Invoke((Action)(() => SomeEventHandler(someparameters))); // invoke itself
else
{
... // put code which should run in UI thread here
}
}
你正在同步上升事件,不可能在另一个完成之前得到一个(据我所知不需要同步)。
我是编码游戏的新手,所以我不太确定如何解决这个问题。我的代码从一个线程中提取信息,该线程从一个 9DOF 传感器中获取数据,该传感器以三个欧拉角的形式出现。然后使用数据生成点,在屏幕上用绘图 class 从这些点创建一个圆圈。 To 完美地工作了一段时间,但最终总是会给出这个异常,说在 System.Drawing.dll 中发生了一个类型为 'System.InvalidOperationException' 的未处理的异常,并附加信息说该对象当前正在别处使用。在仔细研究并尝试绘制的内容后,我得出(可能不正确的)结论,即线程发送数据的速度快于主代码渲染绘图的速度。我怎样才能防止这种情况发生?下面是代码的摘录。
private void button3_Click(object sender, EventArgs e)
{
//single node test
Nodes.NodesList.Add(new RazorIMU("COM7"));
Nodes.NodesList[0].StartCollection('e');
Nodes.NodesList[0].CapturedData += new RazorDataCaptured(Fusion_CapturedData);
public void Fusion_CapturedData(float[] data, float deltaT)
{
int centerX = (int)(300 + (5 / 3) * data[0] + 0.5);
int centerY = (int)(300 + (5 / 3) * data[1] + 0.5);
int endPointX = (int)(centerX + 25 * Math.Sin(Math.PI / 180 * data[2]) + 0.5);
int endPointY = (int)(centerY + 25 * Math.Cos(Math.PI / 180 * data[2]) + 0.5);
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
/*g.DrawLine(new Pen(Color.Yellow), 300, 0, 300, 600);
g.DrawLine(new Pen(Color.Yellow), 0, 300, 600, 300);
g.DrawLine(new Pen(Color.LightYellow), 150, 0, 150, 600);
g.DrawLine(new Pen(Color.LightYellow), 0, 150, 600, 150);
g.DrawLine(new Pen(Color.LightYellow), 450, 0, 450, 600);
g.DrawLine(new Pen(Color.LightYellow), 0, 450, 600, 450);*/
g.DrawEllipse(new Pen(Color.Green), centerX - 25, centerY - 25, 50, 50);
g.DrawLine(new Pen(Color.Green), centerX, centerY, endPointX, endPointY);
}
pictureBox1.Image = bmp;
}
这是主要代码。线程只是以接收信息的速度发送信息,所以我认为我不需要把它放在这里,除非有人另有说明。
{ public 委托 void RazorDataCaptured(float[] 数据, float deltaT);
/// <summary>
///
/// Object for Sparkfun's 9 Degrees of Freedom - Razor IMU
/// Product ID SEN-10736
/// https://www.sparkfun.com/products/10736
///
/// Running Sample Firmware
/// https://github.com/a1ronzo/SparkFun-9DOF-Razor-IMU-Test-Firmware
///
/// </summary>
public class RazorIMU : IDisposable
{
private SerialPort _com;
private static byte[] TOGGLE_AUTORUN = new byte[] { 0x1A };
public string Port { get; private set; }
private Thread _updater;
private string[] _parts;
public event RazorDataCaptured CapturedData = delegate { };
private float[] _data = new float[9];
private static bool running = false;
/// <summary>
/// Create a new instance of a 9DOF Razor IMU
/// </summary>
/// <param name="portName">Serial port name. Ex: COM1.</param>
public RazorIMU(string portName)
{
// Create and open the port connection.
_com = new SerialPort(portName, 57600, Parity.None, 8, StopBits.One);
_com.Open();
Port = portName;
// Set the IMU to automatically collect data if it has not done yet.
//Thread.Sleep(3000);
//_com.Write("#s00");
//_com.DiscardInBuffer();
}
/// <summary>
/// Start continuous collection of data.
/// </summary>
public void StartCollection(char dataType)
{
running = true;
if (dataType == 'r') _updater = new Thread(new ThreadStart(ContinuousCollect));
else if (dataType == 'q') _updater = new Thread(new ThreadStart(ContinuousCollectQuat));
else if (dataType == 'e') _updater = new Thread(new ThreadStart(ContinuousCollectEuler));
_updater.Start();
}
/// <summary>
/// Stop continuous collect of data.
/// </summary>
public void StopCollection()
{
if (_updater != null)
{
running = false;
_updater.Join();
_updater = null;
}
}
/// <summary>
/// This method is extremely important. It continously updates the data array.
/// Data is read and the change in time since the last read is calculated.
/// The CapturedData event is triggered, sending the new data and the change in time.
/// </summary>
private void ContinuousCollect()
{
_com.WriteLine("#osr"); //Sets sensor output data to raw.
_com.ReadLine(); //Discards first line if broken.
while (running) //Static Boolean that controls whether or not to keep running.
{
ReadDataRaw();
CapturedData(_data, 0.020F);
}
}
private void ContinuousCollectQuat()
{
_com.WriteLine("#ot"); //Sets sensor output data to quaternions.
_com.ReadLine(); //Discards first line if broken.
while (running) //Static Boolean that controls whether or not to keep running.
{
ReadDataQuat();
CapturedData(_data, 0.020F);
}
}
private void ContinuousCollectEuler()
{
_com.WriteLine("#ob"); //Sets sensor output data to quaternions.
_com.ReadLine(); //Discards first line if broken.
while (running) //Static Boolean that controls whether or not to keep running.
{
ReadDataEuler();
CapturedData(_data, 0.020F);
}
}
/// <summary>
/// Get a single sample of the 9DOF Razor IMU data.
/// <para>Format: [accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z,mag_x,mag_y,mag_z]</para>
/// </summary>
/// <param name="result">double array of length 9 required.</param>
private void ReadDataRaw()
{
_parts = _com.ReadLine().Split(',');
if (_parts.Length == 9)
for (int i = 0; i < 9; i++)
_data[i] = float.Parse(_parts[i]);
}
private void ReadDataQuat()
{
_parts = _com.ReadLine().Split(',');
if (_parts.Length == 4)
for (int i = 0; i < 4; i++)
_data[i] = float.Parse(_parts[i]);
}
private void ReadDataEuler()
{
_parts = _com.ReadLine().Split(',');
if (_parts.Length == 3)
for (int i = 0; i < 3; i++)
_data[i] = float.Parse(_parts[i]);
}
/// <summary>
///
/// </summary>
public void Dispose()
{
StopCollection();
if (_com != null) //Make sure _com exists before closing it.
_com.Close();
_com = null;
_data = null;
_parts = null;
}
}
}
问题:
- 你必须dispose pens。
- 您正在创建一些由您创建的线程中的上升事件,因此所有事件处理程序在访问 UI 元素之前必须使用
Invoke
。
您可以在 Form
中使用这个简单的事件处理程序模板(将其应用于 Fusion_CapturedData()
):
public void SomeEventHandler(someparameters)
{
if (InvokeRequired)
Invoke((Action)(() => SomeEventHandler(someparameters))); // invoke itself
else
{
... // put code which should run in UI thread here
}
}
你正在同步上升事件,不可能在另一个完成之前得到一个(据我所知不需要同步)。