使用SerialPort.CatchReceivedEvents()为什么线程数不断增加

时间:2013-05-28 01:15:25

标签: c# .net multithreading serial-port c#-2.0

我正在运行带有.NET Framework 2.0的 C#应用程序来读取 SerialPort 中的数据,以便从比例中获得权重。

应用程序工作正常,它应该做什么,但线程数不断增加并且在应用程序崩溃之前消耗更多内存,通常是在大约4小时之后。

使用serialport模拟器运行时,线程数在 30 附近稳定。但是当我使用实际比例时,它会超过 500个线程

我使用Microsoft Managed Stack Explorer 1.0来获取线程转储,而且几乎所有这些都具有以下堆栈:

0. System.IO.Ports.SerialPort.CatchReceivedEvents (Source Unavailable)
1. System.IO.Ports.SerialStream.EventLoopRunner.CallReceiveEvents (Source Unavailable)
2. System.Threading._ThreadPoolWaitCallback.WaitCallback_Context (Source Unavailable)
3. System.Threading.ExecutionContext.Run (Source Unavailable)
4. System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal (Source Unavailable)
5. System.Threading._ThreadPoolWaitCallback.PerformWaitCallback (Source Unavailable)

我无法确定创建这些线程的原因。有没有人知道我在这里缺少什么?谢谢!

这是我的代码:
Scale.cs - >调用方法open()时创建一个线程。线程从getWeight()读取值。
Scales.cs - >在方法SerialPort_DataReceived(...)中处理来自串行端口的事件。这是调用m_SerialPort.ReadLine()的地方,最终为getWeight()提供值。

Scale.cs:

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using ScalesGSS;
    using StateMachine.Exceptions;
    using StateMachine.Log;
    using StateMachine.MessageOutput;

    namespace StateMachine.DriverImplementation
    {

    class Scale : AScale
    {
        private const int Scale_version = 1;

        private Thread thread = null;

        private IScales gScale = null;

        //
        private string m_Type;
        private string m_PortName;
        private int m_BaudRate;
        private char m_Parity;
        private int m_DataBits;
        private string m_StopBits;
        private int m_CommandReturnLength;
        private string m_CommandType;
        private string m_CommandValue;
        private int m_ReadTimeOutInMilliseconds;
        private int m_WeightInitialPosition;
        private int m_WeightFinalPosition;
        private int m_TimeBetweenReadsInMilliseconds;
        private int m_StableReadQuantity;
        private int m_MinimumWeight;
        private int m_ScaleID;
        //
        private double m_OldWeight = 0.0;
        private double m_Offset = 0.0;
        private double m_CurrentWeight = 0.0;
        int m_WeightEqualCount = 0;
        //
        byte m_Status = 3; // "NO COMMUNICATION"
        //
        private bool m_Closed = false;
        private static LogFactory m_Log = new LogFactory(LogCategory.Device, "");
        ErrorDialog m_ErrorDialog = new ErrorDialog();

        public Scale()
        {
            this.setClassName("Scale");
            this.setDeviceType(DeviceType.Scale);
        }

        public void run()
        {
            try
            {

                if (this.m_Type.ToUpper().Equals("GENERICSCALES")) // GENERICSCALES or MOCKSCALES
                    this.gScale = new ScalesGSS.GenericScales();
                else
                    this.gScale = new ScalesGSS.MockScales();

                this.gScale.PortName = this.m_PortName;
                this.gScale.BaudRate = this.m_BaudRate;
                this.gScale.Parity = this.m_Parity.ToString();
                this.gScale.DataBits = this.m_DataBits;
                this.gScale.StopBits = this.m_StopBits;
                this.gScale.CommandReturnLength = this.m_CommandReturnLength;
                this.gScale.CommandType = this.m_CommandType;
                this.gScale.CommandValue = this.m_CommandValue;
                this.gScale.ReadTimeOut = this.m_ReadTimeOutInMilliseconds;
                this.gScale.WeightInitialPosition = this.m_WeightInitialPosition;
                this.gScale.WeightFinalPosition = this.m_WeightFinalPosition;
                this.gScale.setParameters();
                this.gScale.configurePort();

                while (true)
                {
                    if (this.m_Closed)
                    {
                        if (this.OpenedPort())
                            this.gScale.closePort();
                        break;
                    }

                    Thread.Sleep(this.m_TimeBetweenReadsInMilliseconds);

                    if (!this.OpenedPort())
                    {
                        if (!this.OpenPort())
                        {
                            m_Log.writeLogWarning("Error opening serialport.", " Port: " + this.m_PortName, true);
                        }
                    }

                    if (this.ErrorReadingWeight())
                    {
                        m_Log.writeLogWarning("Invalid weight.", " Port: " + this.m_PortName, true);
                    }

                    this.m_CurrentWeight = getWeight();

                    if (!ReadingTimeout())
                    {
                        if (this.m_WeightEqualCount > m_StableReadQuantity)
                        {
                            if (m_CurrentWeight > m_MinimumWeight)
                                m_Status = 2; // "WEIGHT STABLE"
                            else
                            {
                                m_Status = 0; // "SCALE FREE"
                                m_WeightEqualCount = 0;
                            }
                        }
                        else
                        {
                            if (m_CurrentWeight > m_MinimumWeight)
                            {
                                m_Status = 1; // "STABILIZING"

                                if ((this.m_CurrentWeight >= (this.m_OldWeight - this.m_Offset)) && (this.m_CurrentWeight <= (this.m_OldWeight + this.m_Offset)))
                                    this.m_WeightEqualCount++;
                                else
                                    this.m_WeightEqualCount = 0;

                                this.m_OldWeight = this.m_CurrentWeight;
                            }
                            else
                            {
                                m_Status = 0; // "SCALE FREE"
                                m_WeightEqualCount = 0;
                            }
                        }
                    }
                    else
                    {
                        m_WeightEqualCount = 0;
                        m_Status = 3;         // "NO COMMUNICATION"
                        string v_Message = "No communication with scale. Port: " + m_PortName;
                        m_Log.writeLogWarning(v_Message, "", true);
                        AutoClosingMessageBox.Show(v_Message, "Scale", 10000);
                    }
                }
            }
            catch (Exception v_Exception)
            {
                m_Log.writeLogError("run()", v_Exception);
            }
        }

        private bool OpenedPort()
        {
            return this.gScale.OpenedPort;
        }

        private bool OpenPort()
        {
            bool v_OpenPort;
            v_OpenPort = this.gScale.openPort();

            if (!v_OpenPort)
            {
                m_ErrorDialog.getScaleErrorMessage(gScale);
            }

            return v_OpenPort;
        }

        private bool ErrorReadingWeight()
        {
            return this.gScale.ErrorReadingWeight;
        }

        private double getWeight()
        {
            return this.gScale.getWeight();
        }

        private DateTime LastGoodReading()
        {
            return gScale.LastGoodReading;
        }

        private void setLastGoodReading(DateTime p_Value)
        {
            gScale.LastGoodReading = p_Value;
        }

        private bool ReadingTimeout()
        {
            if (m_ReadTimeOutInMilliseconds > 0)
            {
                DateTime v_LastGoodReading = LastGoodReading() == DateTime.MinValue ? DateTime.Now : LastGoodReading();
                setLastGoodReading(DateTime.Now);
                return DateTime.Now > v_LastGoodReading.AddMilliseconds(m_ReadTimeOutInMilliseconds);
            }
            else
                return false;
        }

        #region "IDriverService"

        public override byte getStatus()
        {
            return m_Status;
        }

        public override byte[] read()
        {
            return System.Text.ASCIIEncoding.ASCII.GetBytes(m_CurrentWeight.ToString());
        }

        public override byte[] read(int p_InitialPosition, int p_Size)
        {
            return read();
        }

        public override byte[] write(byte[] p_Data)
        {
            string v_Temp = System.Text.ASCIIEncoding.ASCII.GetString(p_Data);

            if (v_Temp.Equals("getScaleNumber"))
                return System.Text.ASCIIEncoding.ASCII.GetBytes(m_ScaleID.ToString());
            else
                throw new EDriverAccess(1, "Not implemented");
        }

        public override bool open()
        {
            this.thread = new Thread(run);
            this.thread.Name = "SCALE";
            this.thread.IsBackground = true;
            this.thread.Start();
            return true;
        }

        public override bool close()
        {
            try
            {
                this.release();
                return true;
            }
            catch
            {
                return false;
            }
        }

        public override int getVersion()
        {
            return Scale_version;
        }

        public override void setProperties(Dictionary<string, string> p_props)
        {
            try
            {
                this.m_Type = p_props["type"];
                this.m_PortName = p_props["portName"];
                this.m_BaudRate = Int32.Parse(p_props["baudRate"]);
                this.m_Parity = char.Parse(p_props["parity"]);
                this.m_DataBits = Int32.Parse(p_props["dataBits"]);
                this.m_StopBits = p_props["stopBits"];
                this.m_CommandReturnLength = Int32.Parse(p_props["returnLength"]);
                this.m_CommandType = p_props["commandType"];
                this.m_CommandValue = p_props["commandValue"];
                this.m_ReadTimeOutInMilliseconds = Int32.Parse(p_props["readTimeout"]);
                this.m_WeightInitialPosition = Int32.Parse(p_props["weightInitPos"]);
                this.m_WeightFinalPosition = Int32.Parse(p_props["weightFinPos"]);
                this.m_TimeBetweenReadsInMilliseconds = Int32.Parse(p_props["delayLeitura"]);
                this.m_StableReadQuantity = Int32.Parse(p_props["qtdeLeituraEstavel"]);
                this.m_MinimumWeight = Int32.Parse(p_props["pesoMinimo"]);
                this.m_ScaleID = Int32.Parse(p_props["numBalanca"]);
                if (p_props.ContainsKey("precision"))
                    this.m_Offset = Int32.Parse(p_props["precision"]);
            }
            catch (Exception)
            {
                throw new Exception();
            }
        }

        public override void release()
        {
            this.m_Closed = true;
            m_Status = 3; // "NO COMMUNICATION"
        }
        #endregion
    }
}

Scales.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Reflection;
using System.Timers;
using Scales.Util;


namespace Scales.DLL
{
    public class Scales : Status
    {
        public event EventHandler StableWeightChanged;

        protected virtual void OnCountdownCompleted(EventArgs e)
        {
            if (StableWeightChanged != null)
                StableWeightChanged(this, e);

        }

        System.Timers.Timer timerTimeWithoutSample;
        private int m_IntervalsWithoutSample = 0;
        private string m_EndOfWeightChar = "";

        private void _timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            m_IntervalsWithoutSample++;
        }

        public int IntervalsWithoutSample { get { return m_IntervalsWithoutSample; } }

        private SerialPort m_SerialPort;

        public string PortName { get; set; }
        public int BaudRate { get; set; }
        public int DataBits { get; set; }

        private Double m_Weight = 0;
        public Double Weight
        {
            get
            {
                if (m_BufferWeights.Count > 0)
                {
                    try
                    {
                        m_Weight = treatReceivedValue(m_BufferWeights[m_BufferWeights.Count - 1]);
                    }
                    catch
                    {

                    }
                    finally
                    {
                        ErrorReadingWeight = (m_Weight != -1 ? false : true);
                    }

                }
                else
                {
                    m_Weight = 0;
                }
                return m_Weight;
            }
        }

        public List<Double> getAndFlushPastWeights()
        {
            List<Double> v_FlushedValues = new List<double>();

            Double v_WeightCursor;
            while (m_BufferWeights.Count > 1 && v_FlushedValues.Count < 200)
            {
                v_WeightCursor = treatReceivedValue(m_BufferWeights[0]);

                if (v_WeightCursor >= 0)
                {
                    v_FlushedValues.Add(v_WeightCursor);
                }

                m_BufferWeights.RemoveAt(0);
            }
            return v_FlushedValues;
        }

        public void ResetWeights()
        {
            if (m_BufferWeights != null)
            {
                m_BufferWeights.Clear();
            }
        }

        public string NewLineCommandType { get; set; }
        public string NewLineCommand { get; set; }
        public int ReturnLength { get; set; }
        public int WeightInitialPosition { get; set; }
        public int WeightFinalPosition { get; set; }
        public int MotionBitPos { get; set; }

        public int ReadTimeOut { get; set; }
        public bool OpenedPort { get; private set; }
        public bool ErrorReadingWeight { get; private set; }
        public DateTime LastGoodReading { get; private set; }

        public bool IsStable { get; private set; }

        private Parity PortParity { get; set; }
        public string SerialParity
        {
            get { return PortParity.ToString(); }
            set
            {
                setParity(value);
            }
        }

        public int WeightReadLength
        {
            get
            {
                if (WeightFinalPosition >= WeightInitialPosition)
                {
                    return WeightFinalPosition - WeightInitialPosition + 1;
                }
                else
                {
                    return 0;
                }
            }
        }

        private StopBits PortStopBits { get; set; }
        public string SerialStopBits
        {
            get { return PortStopBits.ToString(); }
            set
            {
                setStopBits(value);
            }
        }

        private void setParity(string p_Parity)
        {
            if (p_Parity.Equals(Parity.Even.ToString()))
            {
                PortParity = Parity.Even;
            }
            else if (p_Parity.Equals(Parity.Mark.ToString()))
            {
                PortParity = Parity.Mark;
            }
            else if (p_Parity.Equals(Parity.Odd.ToString()))
            {
                PortParity = Parity.Odd;
            }
            else if (p_Parity.Equals(Parity.Space.ToString()))
            {
                PortParity = Parity.Space;
            }
            else
            {
                PortParity = Parity.None;
            }
        }

        private void setStopBits(string p_StopBits)
        {
            if (p_StopBits.Equals(StopBits.One.ToString()))
            {
                PortStopBits = StopBits.One;
            }
            else if (p_StopBits.Equals(StopBits.OnePointFive.ToString()))
            {
                PortStopBits = StopBits.OnePointFive;
            }
            else if (p_StopBits.Equals(StopBits.Two.ToString()))
            {
                PortStopBits = StopBits.Two;
            }
            else if (p_StopBits.Equals("1"))
            {
                PortStopBits = StopBits.One;
            }
            else if (p_StopBits.Equals("1.5"))
            {
                PortStopBits = StopBits.OnePointFive;
            }
            else if (p_StopBits.Equals("2"))
            {
                PortStopBits = StopBits.Two;
            }
            else
            {
                PortStopBits = StopBits.None;
            }
        }

        public Scales()
        {
            OpenedPort = false;
            ErrorReadingWeight = false;
            IsStable = false;
            m_IntervalsWithoutSample = 999999;
            timerTimeWithoutSample = new System.Timers.Timer(5);
            timerTimeWithoutSample.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
        }

        private int ignoreNextXValues;
        public void resetScale()
        {
            ErrorReadingWeight = false;
            IsStable = false;
            m_IntervalsWithoutSample = 999999;
            ignoreNextXValues = 2;

            m_BufferWeights.Clear();
            m_BufferTime.Clear();

            if (m_SerialPort != null && m_SerialPort.IsOpen)
            {
                m_SerialPort.Close();
                m_SerialPort.Open();
                m_SerialPort.DiscardInBuffer();
            }

        }

        List<String> m_BufferWeights = new List<String>();
        List<String> m_BufferTime = new List<String>();

        public bool openPort()
        {
            try
            {
                if (m_SerialPort.IsOpen)
                {
                    m_SerialPort.Close();
                }

                m_SerialPort.Open();
                resetScale();

                OpenedPort = true;
                return true;
            }
            catch (Exception ex)
            {
                MessageDetail = ex.Message;
                Return = -100;
                OpenedPort = false;
                return false;
            }
        }

        public bool closePort()
        {
            try
            {
                if (m_SerialPort != null)
                {
                    if (m_SerialPort.IsOpen)
                    {
                        m_SerialPort.Close();
                    }
                }
                OpenedPort = false;

                return true;
            }
            catch (Exception ex)
            {
                MessageDetail = ex.Message;
                Return = -101;
                return false;
            }
        }

        public bool configurePort()
        {
            try
            {
                m_SerialPort = new SerialPort();
                m_SerialPort.PortName = PortName;
                m_SerialPort.BaudRate = BaudRate;
                m_SerialPort.Parity = PortParity;
                m_SerialPort.DataBits = DataBits;
                m_SerialPort.StopBits = PortStopBits;
                m_SerialPort.ReadTimeout = ReadTimeOut > 0 ? ReadTimeOut : SerialPort.InfiniteTimeout;
                m_SerialPort.NewLine = getNewLineCommand();
                m_SerialPort.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataReceived);

                return true;
            }
            catch (Exception ex)
            {
                MessageDetail = ex.Message;
                Return = -102;
                return false;
            }
        }

        private string getNewLineCommand()
        {
            string v_Command = string.Empty;

            if (NewLineCommandType.ToUpper().Equals(CommandTypes.CHAR.ToUpper()))
            {
                byte v_Char = Convert.ToByte(NewLineCommand);
                v_Command = Convert.ToChar(v_Char).ToString();
            }
            else if (NewLineCommandType.ToUpper().Equals(CommandTypes.STRING.ToUpper()))
            {
                v_Command = NewLineCommand;
            }
            else
            {
                char[] v_delimiters = { '|' };
                String[] v_Strings = NewLineCommand.Split(v_delimiters);

                if (v_Strings.Length == 2)
                {
                    v_Command = v_Strings[0];
                    m_EndOfWeightChar = v_Strings[1];
                }
                else
                {
                    v_Command = NewLineCommand;
                }
            }

            return v_Command;
        }

        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                LastGoodReading = DateTime.Now;
                string ReadLine = m_SerialPort.ReadLine();
                m_BufferWeights.Add(ReadLine);
            }
            catch (Exception)
            {
                m_Weight = 0;
                LastGoodReading = DateTime.MinValue;
            }
        }

        private Double treatReceivedValue(string p_ReceivedValue)
        {
            try
            {
                if (ignoreNextXValues > 0) ignoreNextXValues--;
                if (ignoreNextXValues > 0) return 0;

                double v_Value = double.MinValue;
                p_ReceivedValue = p_ReceivedValue.Replace("\r", "").Replace("\n", "");

                m_IntervalsWithoutSample = 0;

                if (p_ReceivedValue.Length < WeightInitialPosition + WeightReadLength)
                {
                    return -1;
                }
                if (MotionBitPos != -1 && p_ReceivedValue.Length < MotionBitPos - 1)
                {
                    return -1;
                }

                string strValor = "";

                if (NewLineCommandType.ToUpper().Equals(CommandTypes.VARIABLE_LENGTH.ToUpper()))
                {
                    int v_EndCharPos = p_ReceivedValue.IndexOf(m_EndOfWeightChar);

                    if (v_EndCharPos != -1)
                    {
                        strValor = p_ReceivedValue.Substring(0, v_EndCharPos).Trim();
                    }
                }
                else
                {
                    strValor = p_ReceivedValue.Substring(WeightInitialPosition, WeightReadLength).Trim();
                }

                bool IsDouble = double.TryParse(strValor, out v_Value);

                if (IsDouble)
                {
                    if (MotionBitPos != -1)
                    {
                        string bit = p_ReceivedValue.Substring(MotionBitPos, 1).Trim();
                        if (bit == "1")
                        {
                            IsStable = true;
                        }
                        else IsStable = false;
                    }
                    else
                    {
                        IsStable = true;
                    }

                    return v_Value;
                }
                else
                {
                    return -1;
                }
            }
            catch (Exception ex)
            {
                Return = -200;
                MessageDetail = ex.Message + " - Fonte:readScales";
                ErrorReadingWeight = true;
            }
            return -1;
        }
    }
}

2 个答案:

答案 0 :(得分:2)

我遇到了类似的问题,使用 Serial Port.ReadExisting()而不是 SerialPort.ReadLine()我能够避免创建无限的线程。

答案 1 :(得分:1)

您应该尝试将有问题的代码减少到更易于管理的代码,因为这样可以让其他人更容易调试。那里有很多应用逻辑可能与问题无关,这可能使人们很难看到发生了什么。如果您的示例更短,您将获得更多答案。你甚至可以在这个过程中自己解决问题!

说完之后,我对自己的错误有所预感,但是你需要自己做一些腿部工作来发现我是否已经做错了。是对还是错:

.NET串行端口的工作原理是等待数据进入,然后只要它注意到有新数据就在工作线程上触发DataReceived事件。我相信你有400或500个从未完成工作的工作线程,所以它们永远不会消失。

您的SerialPort.DataReceived事件的事件处理程序看起来像阻止等待整行进入,但只要有一些新的数据,就会触发该事件。串口(不一定是整行)。如果有一长串文本,DataReceived事件将多次触发,每个事件都在其自己的工作线程上。这些工作线程彼此同步,因此他们都将等待前一个完成。

  1. 排队的第一个线程将在m_SerialPort.ReadLine()等待一段时间,直到整行进入。
  2. 随着更多字符的进入,一堆线程在第一个线程后面排队。其余的线程将最终等待第一个线程完成运行事件处理程序。
  3. 最后,整条线路进来。第一个线程结束,排在其后面的5个或6个中的一个开始运行,整个过程开始。
  4. 正在运行的线程在ReadLine上阻塞,在它后面排队5或6个队列。 (我们现在回到1)
  5. 最终排队的线程太多,以至于遇到内存问题。

    您可能将m_SerialPort上的读取超时设置为timeout.Infinite。如果您将超时设置为较小的值,例如1秒(1000)并且您在SerialPort_DataReceived方法中获得了大量TimeoutExceptions,那么我可能是正确的

    旁注您应该在DataReceived事件处理程序中捕获更具体的异常类型。捕获异常可以掩盖这类问题。

    如果我已正确诊断出问题,您需要稍微更改程序的体系结构。最简单的方法是不订阅ReadLine事件并让单个工作线程调用m_SerialPort.ReadLine();无限超时。当它读取一行时,让该工作线程使用接收到的整行文本引发事件并订阅THAT事件而不是SerialPort.DataReceived();事件。

    或者,如果要订阅SerialPort.DataReceived();事件,然后从SerialPort中读取单个字符,直到SerialPort.BytesToRead为零并将它们粘贴在缓冲区中。然后,当你有一整行提升一些&#34; LineReceived&#34;您自己创建的事件会立即将整行返回为EventArgs之一。这种方法并不需要你自己的线程持续很长时间。