c# 使用线程对串口serialPort进行收发数据(四种)

下面将给出“C# 使用线程对串口SerialPort进行收发数据(四种)”的详细攻略。

一、准备工作

在进行串口收发数据的处理前,需要进行一些准备工作:

  1. 引入System.IO.Ports命名空间;
  2. 创建SerialPort对象,配置串口参数;
  3. 确保串口已正常打开。

二、使用线程进行串口数据收发

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为例,在BackgroundWorkerDoWork事件中使用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);
    }
}

三、线程安全

在对串口进行读写操作时,需要注意线程安全。

  1. 对于读操作,先检查缓冲区中的数据是否可供读取,若可读,则读取串口数据;否则等待新数据到达。
  2. 对于写操作,建议使用线程安全的队列来存储将要写入串口的数据,使用线程安全的方式在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技术站

(0)
上一篇 2023年6月7日
下一篇 2023年6月7日

相关文章

  • C#使用正则表达式过滤html标签

    下面是使用C#过滤html标签的完整攻略。 1. 正则表达式 我们知道,HTML标签的特点是以<开头,以>结尾,并且中间可能会有一些属性,例如<div class=”my-class”>。为了过滤掉HTML标签,我们可以使用正则表达式,其中最基础的正则表达式如下: <[^>]+> 这个表达式表示匹配所有以<开头…

    C# 2023年6月7日
    00
  • .NET 中配置从xml转向json方法示例详解

    以下是关于在.NET中配置从XML转向JSON方法示例详解的攻略: 1. 问题描述 在.NET中,我们可以使用XML或JSON格式来配置应用程序。在某些情况下,我们可能需要将XML配置转换为JSON格式。本攻略将介绍如何在.NET中将XML配置转换为JSON。 2. 解决方案 在.NET中,我们可以使用System.Xml.Linq和System.Text.…

    C# 2023年5月12日
    00
  • 详解ASP.NET MVC之下拉框绑定四种方式

    下面我将详细讲解ASP.NET MVC下拉框绑定四种方式的完整攻略。 一、前置知识 在进行下拉框绑定前,需要了解 ASP.NET MVC 的 Razor 语法和 C# 语言基础,以及了解下拉框的 HTML 语法和 ASP.NET MVC 中的 ViewData 和 ViewBag 这两种传值方式。 二、下拉框绑定四种方式 通过 ViewBag 绑定下拉框 在…

    C# 2023年5月31日
    00
  • 基于私钥加密公钥解密的RSA算法C#实现方法

    RSA算法是一种常用的加密技术,在加密和数字签名等领域广泛应用。其基本原理是采用一对密钥(公钥和私钥),使用其中一个密钥对数据进行加密或者签名,使用另一个密钥对数据进行解密或者验证,从而实现加密、解密和数字签名的功能。本文将通过C#代码来讲解如何基于私钥加密公钥解密的RSA算法实现,具体步骤如下: 步骤1:创建密钥对 首先,需要使用C#的RSACryptoS…

    C# 2023年6月7日
    00
  • ASP.NET Core实现中间件的几种方式

    ASP.NET Core 实现中间件的几种方式 ASP.NET Core 是一个跨平台的开源框架,它提供了多种实现中间件的方式。下面是详细的攻略: 步骤1:创建 ASP.NET Core 项目 在 Visual Studio 中创建名为“MiddlewareDemo”的 Core 项目。 步骤2:使用 Use 方法添加中间件 在 Startup.cs 文件中…

    C# 2023年5月12日
    00
  • asp下轻松实现将上传图片到数据库的代码

    下面我将详细讲解如何使用ASP实现将图片上传到数据库的完整攻略,包括以下几个步骤: 创建数据库表 添加上传页面上的表单和相关控件 处理上传文件 将上传的文件保存到数据库中 显示保存的图片 具体步骤如下: 1. 创建数据库表 首先需要创建一个数据库表来存储上传的图片数据。以下是一个示例表格的DDL语句: CREATE TABLE [dbo].[Uploaded…

    C# 2023年6月1日
    00
  • Qt之调用C#的动态库的解决方法

    下面是关于”Qt之调用C#的动态库的解决方法”的完整攻略。 问题描述 Qt是一种跨平台的C++应用程序开发框架,然而有时候需要使用到C#写的动态链接库,此时需要解决Qt调用C#的动态库的问题。 解决方案 Qt虽然没有直接支持调用C#的动态链接库的方法,但是可以通过C++/CLI嵌入C#代码的方式实现调用C#动态库的功能,具体步骤如下: 1. 创建C++/CL…

    C# 2023年5月15日
    00
  • Unity3D实现摄像机镜头移动并限制角度

    下面是针对“Unity3D实现摄像机镜头移动并限制角度”的攻略,分为以下几个步骤: 步骤一:创建摄像机游戏对象 首先,我们需要在Unity3D场景中创建一个摄像机游戏对象。可以在层次视图中点击“Create”按钮,然后选择“Camera”创建一个摄像机。 步骤二:添加脚本 接着,我们需要给刚才创建的摄像机对象添加脚本。在Project视图中创建一个新的脚本文…

    C# 2023年6月3日
    00
合作推广
合作推广
分享本页
返回顶部