下面将给出“C# 使用线程对串口SerialPort进行收发数据(四种)”的详细攻略。
一、准备工作
在进行串口收发数据的处理前,需要进行一些准备工作:
- 引入
System.IO.Ports
命名空间; - 创建
SerialPort
对象,配置串口参数; - 确保串口已正常打开。
二、使用线程进行串口数据收发
1. 使用Thread
类
使用Thread
类可以使代码执行在一个单独的线程上,从而不影响UI的响应。以SerialPort
为例,在线程中使用SerialPort
对串口进行数据读取和写入。
using System.IO.Ports;
using System.Threading;
private SerialPort serialPort;
// 在构造函数中初始化SerialPort并打开串口
public Form1()
{
InitializeComponent();
serialPort = new SerialPort("COM1", 4800, Parity.None, 8, StopBits.One);
serialPort.Open();
}
// 线程函数:读取串口数据
private void ReadSerialPort()
{
while (true)
{
int length = serialPort.BytesToRead;
if (length > 0)
{
byte[] buffer = new byte[length];
serialPort.Read(buffer, 0, length);
// 处理接收到的数据
}
}
}
// 线程函数:写入串口数据
private void WriteSerialPort(byte[] data)
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Write(data, 0, data.Length);
}
}
// 在需要的地方启动串口数据读取线程
Thread readThread = new Thread(ReadSerialPort);
readThread.Start();
// 在需要写入串口数据的地方调用该函数,注意要在UI线程中调用
private void SendMessage()
{
byte[] data = Encoding.ASCII.GetBytes("hello");
Thread writeThread = new Thread(() => WriteSerialPort(data));
writeThread.Start();
}
2. 使用BackgroundWorker
类
BackgroundWorker
类提供了一些简单的方法和事件,使使用线程变得更加容易。它还带有取消和进度更新的支持。以SerialPort
为例,在BackgroundWorker
的DoWork
事件中使用SerialPort
对串口进行数据读取和写入。
using System.ComponentModel;
using System.IO.Ports;
private SerialPort serialPort;
private BackgroundWorker readWorker;
// 在构造函数中初始化SerialPort并打开串口
public Form1()
{
InitializeComponent();
serialPort = new SerialPort("COM1", 4800, Parity.None, 8, StopBits.One);
serialPort.Open();
readWorker = new BackgroundWorker();
readWorker.DoWork += new DoWorkEventHandler(ReadSerialPort);
readWorker.RunWorkerAsync();
}
// 事件处理函数:读取串口数据
private void ReadSerialPort(object sender, DoWorkEventArgs e)
{
while (true)
{
int length = serialPort.BytesToRead;
if (length > 0)
{
byte[] buffer = new byte[length];
serialPort.Read(buffer, 0, length);
// 处理接收到的数据
}
}
}
// 在需要写入串口数据的地方调用该函数,注意要在UI线程中调用
private void SendMessage()
{
byte[] data = Encoding.ASCII.GetBytes("hello");
serialPort.Write(data, 0, data.Length);
}
3. 使用Task
类
Task
类将代替Thread,是一个高层次的异步API,旨在简化异步编程模式并提高可重用性和互操作性。可以使用Task.Run
方法,将一个异步方法运行在新的Task
上下文中。以SerialPort
为例,在Task
中使用SerialPort
对串口进行数据读取和写入。
using System.IO.Ports;
using System.Threading.Tasks;
private SerialPort serialPort;
private CancellationTokenSource readCancellationTokenSource;
// 在构造函数中初始化SerialPort并打开串口
public Form1()
{
InitializeComponent();
serialPort = new SerialPort("COM1", 4800, Parity.None, 8, StopBits.One);
serialPort.Open();
readCancellationTokenSource = new CancellationTokenSource();
Task.Run(() => ReadSerialPortAsync(readCancellationTokenSource.Token));
}
// 异步函数:读取串口数据
private async Task ReadSerialPortAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
int length = serialPort.BytesToRead;
if (length > 0)
{
byte[] buffer = new byte[length];
serialPort.Read(buffer, 0, length);
// 处理接收到的数据
}
await Task.Delay(10);
}
}
// 在需要写入串口数据的地方调用该函数,注意要在UI线程中调用
private void SendMessage()
{
byte[] data = Encoding.ASCII.GetBytes("hello");
serialPort.Write(data, 0, data.Length);
}
4. 使用ThreadPool
类
ThreadPool
是一个多线程执行的技巧,可重复使用的线程池。使用ThreadPool
可避免线程创建和销毁所带来的额外消耗,提高线程利用率。以SerialPort
为例,在ThreadPool
线程池中使用SerialPort
对串口进行数据读取和写入。
using System.IO.Ports;
using System.Threading;
private SerialPort serialPort;
// 在构造函数中初始化SerialPort并打开串口
public Form1()
{
InitializeComponent();
serialPort = new SerialPort("COM1", 4800, Parity.None, 8, StopBits.One);
serialPort.Open();
ThreadPool.QueueUserWorkItem(new WaitCallback(ReadSerialPort));
}
// 线程函数:读取串口数据
private void ReadSerialPort(Object stateInfo)
{
while (true)
{
int length = serialPort.BytesToRead;
if (length > 0)
{
byte[] buffer = new byte[length];
serialPort.Read(buffer, 0, length);
// 处理接收到的数据
}
}
}
// 在需要写入串口数据的地方调用该函数,注意要在UI线程中调用
private void SendMessage()
{
byte[] data = Encoding.ASCII.GetBytes("hello");
ThreadPool.QueueUserWorkItem(new WaitCallback(WriteSerialPort), data);
}
// 线程函数:写入串口数据
private void WriteSerialPort(Object stateInfo)
{
byte[] data = (byte[])stateInfo;
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Write(data, 0, data.Length);
}
}
三、线程安全
在对串口进行读写操作时,需要注意线程安全。
- 对于读操作,先检查缓冲区中的数据是否可供读取,若可读,则读取串口数据;否则等待新数据到达。
- 对于写操作,建议使用线程安全的队列来存储将要写入串口的数据,使用线程安全的方式在
SerialPort.DataReceived
事件中写入队列。
示例代码:
using System.Collections.Concurrent;
using System.IO.Ports;
using System.Threading;
private SerialPort serialPort;
private ConcurrentQueue<byte[]> dataQueue;
// 在构造函数中初始化SerialPort并打开串口
public Form1()
{
InitializeComponent();
serialPort = new SerialPort("COM1", 4800, Parity.None, 8, StopBits.One);
serialPort.Open();
dataQueue = new ConcurrentQueue<byte[]>();
serialPort.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataReceived);
}
// 事件处理函数:串口数据到达
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] buffer = new byte[serialPort.BytesToRead];
serialPort.Read(buffer, 0, buffer.Length);
dataQueue.Enqueue(buffer); // 加入队列
}
// 线程函数:处理串口数据
private void ProcessSerialData()
{
while (true)
{
byte[] data;
if (dataQueue.TryDequeue(out data))
{
// 处理接收到的数据
}
else
{
Thread.Sleep(10); // 队列为空,延时等待
}
}
}
// 在需要写入串口数据的地方调用该函数,注意要在UI线程中调用
private void SendMessage()
{
byte[] data = Encoding.ASCII.GetBytes("hello");
dataQueue.Enqueue(data);
}
// 在需要处理串口数据的地方启动线程
Thread processThread = new Thread(ProcessSerialData);
processThread.Start();
以上就是使用线程进行串口收发数据的详细攻略,希望对您有所帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:c# 使用线程对串口serialPort进行收发数据(四种) - Python技术站