等待DataReceived在不阻塞ui的情况下触发
本文关键字:情况下 ui DataReceived 等待 | 更新日期: 2023-09-27 18:11:47
我需要等待用户向串行端口读取器输入数据,然后处理数据。然而,使用这些代码会阻塞UI,这不是我想要的。关于如何确保在继续之前接收到数据或发生超时,有什么想法吗?
我使用
的原因do
{
Thread.Sleep(1);
} while (...)
是因为没有它,代码在用户有时间更改它之前返回indata
。
我从主函数调用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)
{
;
}
}
这就是多线程、任务、异步编程和/或事件处理程序派上用场的地方。它们都提供了一些东西来帮助你绕过这样的东西,这取决于你正在使用的对象的类型。
在这种情况下,一个好的起点是将整个接收循环作为一个单独的线程运行,然后以某种方式将接收到的数据发送回主线程。
这是一个表单的来源,基本上做了你的工作,但无论是作为Thread
或Task
:
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);
}
}
}
}
我选择了不碰我已经写好的东西的解决方案。相反,我在main函数中添加了这些方法。
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
}