C#中的正确线程技术(使用Timer + Receive Handler + BackgroundWorker)

时间:2014-11-12 15:30:20

标签: c# multithreading

我是线程新手,我正试图在程序中使用它(硬件测试的前端),但是遇到了大量的时间延迟 - 显然我做错了。

我的设置:

我的硬件控制器连接到我的电脑上。我正在通过串行通信来读取4个传感器的值。此通信位于receiveHandler,

private void receiveHandler(object sender, DataStreamEventArgs e)
{
    byte[] dataBytes = e.Response;
    //throws it into a string
    //parses the string with delimiters
    //adds all items to a linked-list "queue"
}

我有一个计时器(System.Timers.Timer),它将计算硬件测试的持续时间,进行一次计算并向控制器询问每个传感器的更新状态。

private void OnTimedEvent(object sender, EventArgs e)
{
    test.duration = ++tickCounter;
    test.ampHoursOut = (test.ampHoursOut * 3600 + test.amperage * 1) / 3600;
    sendToController("CH2.GETANALOG"); //repeat this line once for each sensor
}

我目前有一个BackgroundWorker,它将从控制器中收集数据(我在"队列"在receiveHandler中),并理解它+做一些计算+更新UI。

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = (BackgroundWorker)sender;
    while (!worker.CancellationPending)
    {
        //do a whole lot of logic
    }
}

我的问题:

我的一个控制器输入是读取硬件开关的状态。我注意到,当我按下开关和UI上状态发生变化之间有5秒的延迟。

我的处理必须有很大的延迟。我想BackgroundWorker并没有赶上所有的信息。我每秒都要求提供信息(通过计时器),所以这可能需要放慢速度 - 但我希望有更新的信息每一秒都有准确的测试。

我是否正确使用了计时器,后台工作程序和接收处理程序?可以做些什么来加快我的沟通和处理?

我是一名自学成才的程序员,并且不一定(你可能已经看到)知道所有的基本概念。任何建议将不胜感激:)

谢谢朱莉娅

编辑:代码:

    //variables used (added relevant ones so code is better understood)      
    private static System.Timers.Timer timer;
    LinkedList<string> incomingData = new LinkedList<string>();
    string lastDatum = "";


    /// <summary>
    /// Method that is called when serial data is received from the controller.
    /// The data is received in bytes, converted to a string and parsed at each "::" which represents a new message.
    /// The last 'item' from the data is saved and added to the front of the next set of data, in case it is incomplete.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void receiveHandler(object sender, DataStreamEventArgs e)
    {
        byte[] dataBytes = e.Response;
        string data = lastDatum + System.Text.Encoding.Default.GetString(dataBytes); // reads the next available line coming from the serial port
        UpdateDataTextbox(data);  // prints it out for debuging purposes

        lastDatum = ""; // just in case

        char[] delimiters = new char[] { ':' }; // splits the data at the ":" symbol, deleting empty entries.
        Queue<string> dataQueue = new Queue<string>(data.Split(delimiters,
                 StringSplitOptions.RemoveEmptyEntries));

        while(dataQueue.Count > 1)
        {
            string str = dataQueue.Dequeue().Replace("\r", string.Empty).Replace("\n", string.Empty).Replace(":", string.Empty).Trim(); // remove all useless characters
            if (str != "")
            {
                incomingData.AddLast(str); // add to a queue that can be accessed by the background worker for processing & outputting.
            }
        }
        lastDatum = dataQueue.Dequeue(); // Last data item in a transmission may be incomplete "CH1.GETDA" and thus it is appended to the front of the next list of data.
    }

    /// <summary>
    /// Background Worker thread will be used to continue testing the connection to the controller,
    /// process messages in the incoming message queue (actually a linked list),
    /// and sends new messages for updated data.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = (BackgroundWorker)sender;
        while (!worker.CancellationPending)
        {
            if (!relayBoard.OpenConn())
            {
                MessageBox.Show("Connection to controller has been lost.");
                testInterrupted = "Lost connection. Time = " + test.duration;
                UpdateStatusText(false);
                ShutErDown();
            }

            //update test time & Ah out
            timeSpan = TimeSpan.FromSeconds(tickCounter);
            UpdateDurationTextbox(timeSpan.ToString()); // update display with duration
            UpdateAhTextbox(test.ampHoursOut.ToString());

            /////////////////////////////////////////////////////
            if (incomingData.Count > 0)
            {
                string dataLine = "";
                try
                {
                    dataLine = incomingData.First();
                }
                catch (System.InvalidOperationException emai)
                {
                    break; //data hasn't come in yet, it will by the time this runs next.
                }

                incomingData.RemoveFirst();

                if (dataLine.Contains("GET"))  // some sensor values (analog/temp/digital) has come in
                {
                    if (dataLine.Contains("GETANALOG")) // an analog value has come in
                    {
                        int index = dataLine.IndexOf("CH");
                        int pin = (int)Char.GetNumericValue(dataLine[index + 2]);

                        double value = 0;
                        int dataLineLength = dataLine.Length;
                        if (dataLineLength > 13) // value is appended to end of line
                        {
                            try
                            {
                                value = Convert.ToDouble(dataLine.Substring(13));
                            }
                            catch // can't convert to double
                            {
                                int index2 = dataLine.IndexOf("CH", 3);
                                if (index2 != -1) // there happen to be two sets of commands stuck together into one
                                {
                                    string secondHalf = dataLine.Substring(index2);
                                    incomingData.AddFirst(secondHalf);
                                }
                            }
                        }
                        else // value is on the next line
                        {
                            try
                            {
                                value = Convert.ToDouble(incomingData.First());
                                incomingData.RemoveFirst();
                            }
                            catch // can't convert to double
                            {
                                MessageBox.Show("Error occured: " + dataLine);
                            }
                        }
                        switch (pin)
                        {
                            case 1:
                                ReadVoltage(value);
                                break;
                            case 2:
                                ReadAmperage(value);
                                break;
                        }
                    }
                    else if (dataLine.Contains("GETTEMP")) // received reply with temperature data
                    {
                        int index = dataLine.IndexOf("CH");
                        int pin = (int)Char.GetNumericValue(dataLine[index + 2]); // using index of CH, retrieve which pin this message is coming from

                        double value = 0;
                        int dataLineLength = dataLine.Length;
                        if (dataLineLength > 11) // value is appended to end of line
                        {
                            try
                            {
                                value = Convert.ToDouble(dataLine.Substring(11));
                            }
                            catch // can't convert to double
                            {
                                int index2 = dataLine.IndexOf("CH", 3);
                                if (index2 != -1) // there happen to be two sets of commands stuck together into one
                                {
                                    string secondHalf = dataLine.Substring(index2);
                                    incomingData.AddFirst(secondHalf);
                                }
                            }
                        }
                        else // value is on the next line
                        {
                            value = Convert.ToDouble(incomingData.First());
                            incomingData.RemoveFirst();
                        }
                        ReadTemperature(pin, value);
                    }
                    else // must be CH3.GET
                    {
                        int index = dataLine.IndexOf("CH");
                        int pin = (int)Char.GetNumericValue(dataLine[index + 2]); // using index of CH, retrieve which pin this message is coming from

                        if (pin == 3) // only care if it's pin 3 (BMS), otherwise it's a mistake
                        {
                            double value = 0;
                            int dataLineLength = dataLine.Length;
                            if (dataLineLength > 7) // value is appended to end of line
                            {
                                try
                                {
                                    value = Convert.ToDouble(dataLine.Substring(7));
                                }
                                catch // can't convert to double
                                {
                                    int index2 = dataLine.IndexOf("CH", 3);
                                    if (index2 != -1) // there happen to be two sets of commands stuck together into one
                                    {
                                        string secondHalf = dataLine.Substring(index2);
                                        incomingData.AddFirst(secondHalf);
                                    }
                                }
                            }
                            else // value is on the next line
                            {
                                value = Convert.ToDouble(incomingData.First());
                                incomingData.RemoveFirst();
                            }
                            ReadBMS(value);
                        }
                    }
                }
                else if (dataLine.Contains("REL")) // received reply about relay turning on or off.
                {
                    if (dataLine.Contains("RELS")) // all relays turning on/off
                    {
                        if (dataLine.Contains("ON"))
                        {
                            for (int pin = 1; pin <= 4; pin++)
                            {
                                test.contactors[pin] = true;
                            }
                        }
                        else // "OFF"
                        {
                            for (int pin = 1; pin <= 4; pin++)
                            {
                                test.contactors[pin] = false;
                            }
                        }
                    }
                    else // single relay is turning on/off
                    {
                        int index = dataLine.IndexOf("REL");
                        int pin = (int)Char.GetNumericValue(dataLine[index + 3]);

                        if (dataLine.Contains("ON"))
                        {
                            test.contactors[pin] = true;
                        }
                        else if (dataLine.Contains("OFF"))
                        {
                            test.contactors[pin] = false;
                        }
                        else if (dataLine.Contains("GET"))
                        {
                            if (Convert.ToInt32(incomingData.First()) == 1)
                                test.contactors[pin] = true;
                            else
                                test.contactors[pin] = false;
                            incomingData.RemoveFirst();
                        }
                    }
                }
            }
        }
    }

    /// <summary>
    /// Main timer used to log the duration of the test, calculate amp hours
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnTimedEvent(object sender, EventArgs e)
    {
        test.duration = ++tickCounter;

        // calculate Ah
        test.ampHoursOut = (test.ampHoursOut * 3600 + test.amperage * 1) / 3600;

        //read & output v, a, bms state
        //sendToController("CH1.GETANALOG"); // get voltage
        sendToController("CH2.GETANALOG"); // get amperage
        sendToController("CH3.GET"); // get BMS state

        //read & output temperature
        sendToController("CH4.GETTEMP"); // get temperature
        sendToController("CH5.GETTEMP");
    }

0 个答案:

没有答案