【问题标题】:InvalidOperationException in thread based drawing基于线程的绘图中的 InvalidOperationException
【发布时间】:2016-07-05 23:19:39
【问题描述】:

我是编码游戏的新手,所以我不太确定如何解决这个问题。我的代码从一个线程中提取信息,该线程从一个 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;
        }

这是主要代码。线程只是以接收到的速度发送信息,所以我认为我不需要把它放在这里,除非有人另有说明。

{ 公共委托 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;
    }

}

}

【问题讨论】:

  • 线程如何导致重绘?可能是缺少Invoke 或同步(例如异步调用事件,然后在前一个事件完成之前过早再次调用它)。
  • 我认为这正是它正在做的事情,因为当我更改绘制的行数时,它会影响它在抛出此异常之前运行的时间。
  • 好吧,我对 Invoke 做了一些粗略的研究(同样,非常新,完全是自学的),但这似乎不能解决问题,因为数据是连续出现的溪流。似乎更多的是同步将是答案。它可以做些什么来解决这个问题?我想我需要一种方法来阻止我的 sn-p 代码完成并简单地从下一批数据重新开始。
  • 你能展示从接收数据到出现在屏幕上的完整链吗?目前你只发布了一些绘图和使用 UI 的事件处理程序(所以它应该在 UI 线程中运行),但不清楚是谁引发了这个事件,如何以及何时。
  • 好的,我试试看。

标签: c# multithreading drawing


【解决方案1】:

问题:

  1. 你必须dispose pens
  2. 您在线程创建的某些事件中上升事件,因此所有事件处理程序必须在访问 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
    }
}

你正在同步上升事件,在另一个完成之前不可能得到一个(据我所知不需要同步)。

【讨论】:

  • 谢谢伙计!该调用实际上对解决问题有什么作用?我想知道,所以我知道下次使用它。
猜你喜欢
  • 2021-12-29
  • 2017-04-05
  • 2020-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-21
相关资源
最近更新 更多