等待DataReceived在不阻止ui的情况下触发

时间:2014-07-07 08:03:22

标签: c# multithreading serial-port

我需要等待用户将数据输入到串口读取器,然后处理数据。但是,使用此代码会阻止不是我想要的UI。在继续之前,有关如何确保收到数据或超时的任何想法?

我使用的原因

do
{
Thread.Sleep(1);
} while (...)

是因为没有它,代码会在用户有时间更改之前返回indata

我从main函数调用ReadFromSerial并在那里处理数据。如果出现任何问题,我希望它返回一个空字符串。

public string ReadFromSerial()
{
    try
    {
        System.IO.Ports.SerialPort Serial1 = new System.IO.Ports.SerialPort("COM1", 9600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One);

        var MessageBufferRequest = new byte[13] { ... };
        int BufferLength = 13;

        if (!Serial1.IsOpen)
        {
            Serial1.Open();
        }

        Serial1.Write(MessageBufferRequest, 0, BufferLength); //Activates the serialport reader

        indata = "";

        Stopwatch timer = new Stopwatch();
        timer.Start();
        Serial1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
        do
        {                    
            Thread.Sleep(1);
        } while (string.IsNullOrEmpty(indata) && timer.Elapsed.TotalSeconds < 10);
        timer.Stop();

        if (Serial1.IsOpen)
        {
            Serial1.Close();
        }

        return indata;

    }
    catch (Exception ex)
    {
        return "";
    }
}

private static string indata;

private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
    try
    {
        SerialPort sp = (SerialPort)sender;
        if (sp.BytesToRead > 0)
        {
            indata = sp.ReadExisting();
        }
    }
    catch(InvalidOperationException)
    {
        ;
    }
}

2 个答案:

答案 0 :(得分:1)

这是多线程,任务,异步编程和/或事件处理程序派上用场的地方。所有这些都可以帮助你解决这类问题,具体取决于你正在使用的对象类型。

在这种情况下,一个好的起点是将整个接收循环作为一个单独的线程运行,然后以某种方式将接收的数据发送回主线程。

以下是表单的来源,它基本上与您的表现相同,但作为ThreadTask

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    // Button: starts Task version
    private void button1_Click(object sender, EventArgs e)
    {
        StartReceiveTask();
    }

    // Button: starts Thread version
    private void button2_Click(object sender, EventArgs e)
    {
        StartReceiveThread();
    }


    // Start the Receive loop as a Task
    public void StartReceiveTask()
    {
        System.Threading.Tasks.Task.Run(() => receiveThreadFunc());
    }

    // Start the Receive loop as a Thread
    public void StartReceiveThread()
    {
        var thd = new System.Threading.Thread(receiveThreadFunc);
        thd.Start();
    }

    // Called when the Receive loop finishes
    public void DataReceived(string data)
    {
        // do something with the data here
    }

    // The Receive loop, used by both Thread and Task forms.
    public void receiveThreadFunc()
    {
        using (var serial1 = new System.IO.Ports.SerialPort("COM1", 9600, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One))
        {
            // open serial port
            if (!serial1.IsOpen)
                serial1.Open();

            // send init command
            var initCommand = new byte[13];
            serial1.Write(initCommand, 0, initCommand.Length);

            // get start time
            DateTime start = DateTime.Now;
            // buffer for pushing received string data into
            StringBuilder indata = new StringBuilder();

            // loop until at most 10 seconds have passed 
            while ((DateTime.Now - start).TotalSeconds < 2)
            {
                if (serial1.BytesToRead > 0)
                {
                    // allocate a buffer, up to 1K in length, to receive into
                    int blen = Math.Min(1024, serial1.BytesToRead);
                    byte[] buffer = new byte[blen];
                    // read chunks of data until none left
                    while (serial1.BytesToRead > 0)
                    {
                        int rc = serial1.Read(buffer, 0, blen);
                        // convert data from ASCII format to string and append to input buffer
                        indata.Append(Encoding.ASCII.GetString(buffer, 0, rc));
                    }
                }
                else
                    System.Threading.Thread.Sleep(25);

                // check for EOL
                if (indata.Length > 0 && indata.ToString().EndsWith("\r\n"))
                    break;
            }

            if (indata.Length > 0)
            {
                // post data to main thread, via Invoke if necessary:
                string data = indata.ToString();
                if (this.InvokeRequired)
                    this.Invoke(new Action(() => { DataReceived(data); }));
                else
                    this.DataReceived(data);
            }
        }
    }
}

答案 1 :(得分:0)

我选择解决方案而不是触及我已经写过的内容。相反,我在主函数中添加了这些方法。

private void StartReceiveThread()
{
    var thd = new System.Threading.Thread(receiveThreadFunc);
    thd.Start();
}

private void receiveThreadFunc()
{
    string str = Read.ReadFromSerial();
    DataReceived(str);
}

private void DataReceived(string data)
{
    //Process the data received
}
相关问题