基于线程的绘图中的InvalidOperationException

时间:2016-07-05 14:27:40

标签: c# multithreading drawing

我是编码游戏的新手,所以我不太清楚如何解决这个问题。我的代码从一个线程中提取信息,该线程从9DOF传感器中获取数据,该传感器以三个欧拉角出现。然后使用该数据生成点,在该点上使用绘图类在屏幕上创建圆。要完美地工作一段时间,但最终总会给出这个异常,说在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 delegate void RazorDataCaptured(float [] data,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;
    }

}

}

1 个答案:

答案 0 :(得分:0)

问题:

  1. 您必须dispose pens
  2. 您正在使用线程创建的某些事件中升级事件,因此所有事件处理程序必须在访问UI元素之前使用Invoke
  3. 您可以将此简单模板用于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
        }
    }
    

    你正在同步上升事件,它不可能在另一个完成之前完成(就我所见,不需要同步)。