2

我正在使用 .NET Framework 2.0运行C#应用程序,以从SerialPort读取数据以获取秤的重量。

应用程序工作正常,做它应该做的事,但线程数量不断增加,并且消耗更多内存,直到应用程序崩溃,通常在大约 4 小时后。

使用串行端口模拟器运行时,线程数稳定在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() 的地方。

比例尺.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;
        }
    }
}
4

2 回答 2

2

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

话虽如此,我对哪里出了问题有预感,但你需要自己做一些腿部工作来发现我是对还是错:

.NET 串行端口的工作原理是等待数据进入,然后在发现有新数据时在工作线程上触发 DataReceived 事件。我相信您有 400 或 500 个这些工作线程永远不会完成它们的工作,因此它们永远不会消失。

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

  1. 排队的第一个线程将在 m_SerialPort.ReadLine() 等待一段时间,直到整行进入。
  2. 随着更多字符的进入,一堆线程在第一个线程后面排队。其余线程最终将等待第一个线程完成运行您的事件处理程序。
  3. 最后,整行进入。第一个线程完成,在它后面排队的 5 或 6 个线程中的一个开始运行,整个过程重新开始。
  4. ReadLine 上正在运行的线程阻塞,后面还有 5 或 6 个队列。(我们现在回到 1)

最终你有太多的线程排队,你会遇到内存问题。

您可能将 m_SerialPort 上的读取超时设置为 timeout.Infinite。如果您将超时设置为较小的值,例如 1 秒 (1000),并且您的SerialPort_DataReceived方法中有很多 TimeoutExceptions,那么我可能是对的

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

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

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

于 2013-06-25T17:43:24.410 回答
2

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

于 2013-07-30T18:42:51.313 回答