展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > 基于C#实现多线程串口通信的完整代码

基于C#实现多线程串口通信的完整代码

基于C#实现的多线程串口通信方案,通过双缓冲队列、异步读写、锁机制及异常自动恢复解决数据竞争。采用ConcurrentQueue与SemaphoreSlim保障线程安全,ObjectPool降低内存开销,Parallel.ForEach加速批量处理,大幅提升系统吞吐量与稳定性,适用于工业自动化场景。

本文目录

    做过串口通信的人都知道,真正决定系统稳定性的,往往不在于功能实现了多少,而在于多线程环境下如何处理数据竞争和死锁。简单来说,这套方案的思路是:用多线程实现并行数据处理,用锁机制保证临界区安全,再用队列解耦数据流。具体来说,包括了双缓冲队列、异步读写、异常自动恢复、内存池优化等几个核心设计点。下面结合实际代码,逐个拆解。

    一、核心架构设计

    基于C#实现多线程串口通信的完整代码

    整个系统的架构可以看作三层:最底层是物理串口操作,中间层用线程和队列做数据调度,上层通过事件和异步方法暴露给业务逻辑。这样分层的好处是,一旦出了问题,排查范围很容易锁定在某一层。

    二、关键代码实现

    先看串口管理类的核心结构。这里使用了 ConcurrentQueue 做数据缓冲,配合 AutoResetEvent 做写线程的唤醒信号,同时用 SemaphoreSlim 保证串口访问的原子性。需要注意的一点是,DataReceived 事件里直接读取全部可用字节,然后入队并触发回调——这个设计虽然简单,但在高频场景下可能带来事件风暴,后面会讲到怎么优化。

    using System.IO.Ports;
    using System.Threading;
    using System.Collections.Concurrent;
    
    public class SerialPortManager : IDisposable
    {
        private SerialPort _serialPort;
        private Thread _readThread;
        private Thread _writeThread;
        private ConcurrentQueue _dataQueue = new ConcurrentQueue();
        private AutoResetEvent _writeEvent = new AutoResetEvent(false);
        private SemaphoreSlim _serialLock = new SemaphoreSlim(1, 1);
    
        public event Action DataReceived;
    
        public SerialPortManager(string portName, int baudRate)
        {
            InitializePort(portName, baudRate);
            StartThreads();
        }
    
        private void InitializePort(string portName, int baudRate)
        {
            _serialPort = new SerialPort(portName, baudRate)
            {
                Parity = Parity.None,
                DataBits = 8,
                StopBits = StopBits.One,
                ReadTimeout = 500,
                WriteTimeout = 500
            };
            
            _serialPort.DataReceived += (s, e) => 
            {
                var buffer = new byte[_serialPort.BytesToRead];
                _serialPort.Read(buffer, 0, buffer.Length);
                _dataQueue.Enqueue(buffer);
                DataReceived?.Invoke(buffer);
            };
        }
    
        private void StartThreads()
        {
            _readThread = new Thread(ProcessIncomingData);
            _writeThread = new Thread(ProcessOutgoingData);
            _readThread.Start();
            _writeThread.Start();
        }
    
        public async Task SendDataAsync(byte[] data)
        {
            await _serialLock.WaitAsync();
            try
            {
                _writeEvent.Set();
                _serialPort.Write(data, 0, data.Length);
            }
            finally
            {
                _serialLock.Release();
            }
        }
    
        private void ProcessIncomingData()
        {
            while (!_serialPort.IsOpen) Thread.Sleep(100);
            
            while (true)
            {
                if (_dataQueue.TryDequeue(out var data))
                {
                    ParseData(data);
                }
                Thread.Sleep(10);
            }
        }
    
        private void ProcessOutgoingData()
        {
            while (true)
            {
                _writeEvent.WaitOne();
                // 发送队列处理逻辑
            }
        }
    
        private void ParseData(byte[] data)
        {
            // 实现具体协议解析
        }
    
        public void Dispose()
        {
            _serialLock.Dispose();
            _writeEvent.Dispose();
            _serialPort?.Close();
            _serialPort?.Dispose();
        }
    }

    三、线程同步机制

    1. 双缓冲队列设计

    单队列在高频接收时容易造成读写线程互相等待,双缓冲的思路是把接收和处理的队列分开,降低锁竞争。下面这段代码展示了最基本的双缓冲模式:一个线程负责从串口读数据,入队到 _readBuffer;另一个线程从这个队列里取数据做初步处理,再放入 _processBuffer。

    private ConcurrentQueue _readBuffer = new ConcurrentQueue();
    private ConcurrentQueue _processBuffer = new ConcurrentQueue();
    
    void ReceiveThread()
    {
        while (true)
        {
            if (_serialPort.BytesToRead > 0)
            {
                var buffer = new byte[_serialPort.BytesToRead];
                _serialPort.Read(buffer, 0, buffer.Length);
                _readBuffer.Enqueue(buffer);
            }
            Thread.Sleep(1);
        }
    }
    
    void ProcessThread()
    {
        while (true)
        {
            if (_readBuffer.TryDequeue(out var data))
            {
                _processBuffer.Enqueue(ProcessData(data));
            }
        }
    }

    2. 异步读写实现

    用 async/await 替代同步阻塞,既能利用底层 I/O 完成端口,又能避免 Thread.Sleep 带来的 CPU 空转。这里用 SemaphoreSlim 做异步锁,确保同一时刻只有一个操作在执行。需要注意的是,取消令牌 CancellationToken 建议在应用层传入,这样上层可以优雅关闭连接,而不是强制中断线程。

    public async Task ReadAsync(CancellationToken ct)
    {
        await _serialLock.WaitAsync(ct);
        try
        {
            var buffer = new byte[1024];
            int bytesRead = await _serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length, ct);
            Array.Resize(ref buffer, bytesRead);
            return buffer;
        }
        finally
        {
            _serialLock.Release();
        }
    }
    
    public async Task WriteAsync(byte[] data, CancellationToken ct)
    {
        await _serialLock.WaitAsync(ct);
        try
        {
            await _serialPort.BaseStream.WriteAsync(data, 0, data.Length, ct);
        }
        finally
        {
            _serialLock.Release();
        }
    }

    四、异常处理

    1. 异常捕获框架

    串口通信的异常类型相对集中:超时、设备断开、缓冲区溢出。异常处理的原则是让系统在出错后尽量恢复,而不是直接崩溃。下面的示例在捕获 TimeoutException 时做超时重试,捕获 IOException 时认为设备断开,触发重连流程。注意 finally 里的 Release 是一定要执行的,否则锁住后整个通信会卡死。

    public void SafeWrite(byte[] data)
    {
        try
        {
            _serialLock.Wait();
            _serialPort.Write(data, 0, data.Length);
        }
        catch (TimeoutException ex)
        {
            HandleTimeout();
        }
        catch (IOException ex)
        {
            HandleDeviceDisconnect();
        }
        finally
        {
            _serialLock.Release();
        }
    }

    2. 自动重连机制

    设备断开后重连,关键是重连前要释放掉所有旧资源,否则新连接可能被之前的残留状态影响。下面这块代码在进入重连前先加锁,关闭旧端口,等待一个短暂的间隔(这里设为1秒),然后重新初始化。这里有一个细节:重连完成后要重新启动读写线程,因为之前的线程可能已经在异常状态中退出了。

    private void HandleDeviceDisconnect()
    {
        _serialLock.Wait();
        try
        {
            if (_serialPort.IsOpen) _serialPort.Close();
            Thread.Sleep(1000);
            ReinitializePort();
        }
        finally
        {
            _serialLock.Release();
        }
    }
    
    private void ReinitializePort()
    {
        InitializePort(_config.PortName, _config.BaudRate);
        StartThreads();
    }

    五、性能优化

    1. 内存池管理

    频繁分配 byte[] 会给 GC 造成很大压力,尤其是在高速通信场景下。用 ObjectPool 将固定大小的缓冲区循环使用,可以有效减少内存碎片和 GC 暂停。这里的池大小设为10、每个缓冲区1024字节,实际项目中可以根据吞吐量调整。

    private ObjectPool _bufferPool = new ObjectPool(() => new byte[1024], 10);
    
    byte[] GetBuffer() => _bufferPool.Get();
    void ReturnBuffer(byte[] buffer) => _bufferPool.Return(buffer);

    2. 批量数据处理

    当需要处理大量数据帧时,单线程循环处理会成为瓶颈。这时可以借助 Parallel.ForEach 做并行解析,解析完成后再加锁写入结果集。注意这里的锁粒度要尽量小——只在写入 _processedData 的瞬间加锁,解析过程完全并行。

    public void ProcessBatchData(IEnumerable dataArray)
    {
        Parallel.ForEach(dataArray, data =>
        {
            var parsed = ParseData(data);
            lock (_processedData)
            {
                _processedData.Add(parsed);
            }
        });
    }

    六、完整使用示例

    把前面所有模块组合起来,实际使用流程很清晰:先初始化管理器,注册数据接收事件,然后异步发送数据,同时开一个后台任务持续取处理后的数据更新 UI。这里面有一个容易被忽略的点——回调事件和 UI 线程的关系,建议在事件处理里做线程切换,否则跨线程操作控件会抛异常。

    var manager = new SerialPortManager("COM3", 115200);
    
    await manager.SendDataAsync(Encoding.UTF8.GetBytes("Hello"));
    
    manager.DataReceived += (data) => 
    {
        var text = Encoding.UTF8.GetString(data);
        Console.WriteLine($"Received: {text}");
    };
    
    Task.Run(() => 
    {
        while (true)
        {
            if (manager.TryDequeueProcessedData(out var data))
            {
                UpdateUI(data);
            }
        }
    });

    七、调试与监控

    1. 日志记录

    生产环境下,日志是排查问题的最直接手段。建议每条日志带上毫秒级时间戳,这样在分析时序问题时非常有用。下面的写法虽然简单,但已经满足大多数场景。如果日志量特别大,可以考虑异步写入或环形缓冲区,避免 I/O 成为瓶颈。

    public void LogCommunication(string message)
    {
        lock (_logLock)
        {
            File.AppendAllText("com_log.txt", 
                $"{DateTime.Now:HH:mm:ss.fff} - {message}{Environment.NewLine}");
        }
    }

    2. 实时状态监控

    除了日志,实时状态数据也很重要。下面的 SerialStatus 类记录了发送和接收的字节数,可以在 UI 上展示流量波形,异常时直观看到是否出现数据断流或突发增长。

    public class SerialStatus
    {
        public int BytesSent { get; private set; }
        public int BytesReceived { get; private set; }
        
        public void IncrementSent(int count) => BytesSent += count;
        public void IncrementReceived(int count) => BytesReceived += count;
    }

    八、扩展功能实现

    1. 协议解析器

    Modbus RTU 是工业通信中最常见的协议之一。解析器需要处理 CRC 校验、地址匹配、功能码分发等逻辑。下面是一个骨架代码,实际使用时需要按照协议文档逐字节解析。

    public class ModbusParser
    {
        public static Dictionary ParseFrame(byte[] data)
        {
            // 实现Modbus RTU协议解析
            return new Dictionary();
        }
    }

    2. 加密传输

    某些场景下串口数据需要加密传输,比如 GPS 定位数据或敏感配置参数。使用 AES 加密时,需要注意密钥管理和初始化向量的处理。下面这个示例只是一个框架,实际部署时密钥不应硬编码在代码中。

    public class SecureSerialPort
    {
        private Aes _aes = Aes.Create();
        public byte[] Encrypt(byte[] data)
        {
            using (var encryptor = _aes.CreateEncryptor())
            using (var ms = new MemoryStream())
            {
                cs.Write(data, 0, data.Length);
                return ms.ToArray();
            }
        }
    }

    九、最佳实践建议

    线程模型选择

    • 优先使用 Task 替代传统 Thread,利用 .NET 的线程池管理生命周期。
    • 高频数据采用 BlockingCollection 实现生产者-消费者模式,它的内部使用了高效的等待机制,比手动轮询更友好。

    资源管理规范

    using (var manager = new SerialPortManager())
    {
        // 自动释放资源
    }

    性能监控指标

    • 串口缓冲区占用率:过低说明读取不及时,过高说明处理速度跟不上。
    • 数据处理延迟:从串口接收到数据被业务逻辑取走的时间,超过某个阈值需要优化队列或解析逻辑。
    • 异常发生频率:短时间内频繁出现超时或断开,大概率是硬件或线缆问题,不是代码能解决的。
    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    精品专题 更多
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    macOS软件

    正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

    IOS软件

    正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

    AI

    正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw macOS版
    AionClaw macOS版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy macOS版
    Loomy macOS版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。

    AirBuddy 3 macOS版
    AirBuddy 3 macOS版

    AirBuddy 3 是面向 macOS 的蓝牙外设增强工具,可集中查看 AirPods、Beats 与苹果设备电量,提供快速连接、播放控制、电量提醒、桌面小组件和多台 Mac 外设接力等功能。

    AirServer macOS版
    AirServer macOS版

    AirServer macOS版可把 Mac 转换为无线投屏接收端,通过 AirPlay 或 Google Cast 接收手机、平板和电脑画面,并支持多设备同时连接、高清镜像及会话录制,适合家庭娱乐、课堂教学、会议演示和应用展示。

    AnyDesk macOS版
    AnyDesk macOS版

    AnyDesk macOS版是一款适用于远程办公、设备维护和客户支持的远程桌面工具,可连接并操作异地计算机,支持文件传输、无人值守访问、会话权限控制及跨平台协作。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

    讯飞绘文 网页版
    讯飞绘文 网页版

    讯飞绘文网页版是科大讯飞推出的AI图文创作与内容运营工具,覆盖热点选题、文章生成、稿件校对、智能配图、排版发布和数据复盘等环节,适合自媒体创作者及内容团队在线使用。

    Maya(Autodesk Maya) Windows版
    Maya(Autodesk Maya) Windows版

    Autodesk Maya 是面向影视、电视、动画和游戏行业的专业三维制作软件,集多边形与 NURBS 建模、角色绑定、关键帧和动作捕捉动画、Bifrost 特效模拟、材质开发及 Arnold 渲染于一体,

    Aperty Windows版
    Aperty Windows版

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    立刻MV 网页版
    立刻MV 网页版

    立刻MV 面向音乐人、内容创作者和品牌团队,帮助用户低成本完成歌曲 MV、歌词视频、AI 分镜、图生视频与最终合成。

    蛙蛙写作 网页版
    蛙蛙写作 网页版

    蛙蛙写作 AI 创作工具,覆盖小说、剧本、文案,一键生成文稿,打通文字转视频全流程,高效解决创作难题。

    超级简历WonderCV 网页版
    超级简历WonderCV 网页版

    超级简历WonderCV是面向应届生和AI小白的智能求职平台,覆盖简历、面试与职业规划。

    音述AI 网页版
    音述AI 网页版

    音述AI,是一个以“用声音述说故事”为核心的 AI 音乐创作与声音分享社区。

    AionClaw Windows版
    AionClaw Windows版

    AionClaw 是可在桌面端运行的任务型 AI 助手,通过数字专家与技能市场组合办公、创作、数据和开发能力。

    Loomy Windows版
    Loomy Windows版

    Loomy 是科大讯飞推出的桌面 AI 助手,可通过对话、任务与工作目录协助处理办公、内容创作和资料整理。