#c# #multithreading #serial-port
#c# #многопоточность #последовательный порт
Вопрос:
Мне нужно дождаться, пока пользователь введет данные в программу чтения serialport, а затем обработать данные. Однако использование этого кода блокирует пользовательский интерфейс, который не является тем, что я хочу. Есть идеи о том, как убедиться, что данные получены или истек тайм-аут, прежде чем продолжить?
Причина, по которой я использую
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) amp;amp; 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)
{
;
}
}
Ответ №1:
Вот где пригодятся многопоточность, задачи, асинхронное программирование и / или обработчики событий. Все они предлагают что-то, что поможет вам обойти подобные вещи, в зависимости от типов объектов, которые вы используете.
Хорошей отправной точкой в этом случае было бы запустить весь цикл приема как отдельный поток, а затем каким-либо образом отправить полученные данные обратно в основной поток.
Вот источник формы, которая делает в основном то же, что и ваша, но либо в виде 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 amp;amp; indata.ToString().EndsWith("rn"))
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. Я решил использовать потоковую обработку. Спасибо, что показали, как это делается
Ответ №2:
Я принял решение не трогать то, что я уже написал. Вместо этого я добавил эти методы в свою основную функцию.
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
}