• 使用队列实现串口实时读写


    好久没写博客了。上一次还是2015年。。。。

    写C#同时读写串口,也就是发送读取、写入到串口,之前的做法,定时循环读取,又需要的地方写入,而且加lock

     1 public void Read(){
     2     while(true){
     3        lock(this)
     4          SerialPort.Write(读取的命令内容1);  
     5        Thread.sleep(500); 
     6        lock(this) 
     7             SerialPort.Write(读取的命令内容2);  
     8        Thread.sleep(500); 
     9        //..可能还有....
    10     }
    11      
    12 }
    13 
    14 public void Write1(){
    15   lock(this)
    16        SerialPort.Write(写入的命令内容);      
    17 }
    18 
    19 public void Write2(){
    20   lock(this)
    21        SerialPort.Write(写入的命令内容);      
    22 }

    后来,参考网上的前辈思路,用队列,生产者/消费者模式,把需要写入串口的命令放到队列里(Quere<T>),用一个线程专门负责用队列里取出数据并写入串口,这个思路不错。试了一下

    这里主要是用modbus slave工具,模拟串口工具VSPD做一组模拟串口,slave工具初始化,C#去读写数据。

    一开始队列使用

      1 private static Queue<byte[]> cmdList = new Queue<byte[]>(); 

    读取/写入的入队

     1 /// <summary>
     2         /// 读取寄存器状态,200ms读一次
     3         /// </summary>
     4         /// <returns></returns>
     5         static async Task Read()
     6         {
     7             while (true)
     8             {
     9                 //从0,读10个寄存器
    10                 byte[] send = new byte[]
    11                 {
    12                  0x01,0x03,0,0,0,0x0A
    13                 };
    14                 Enqueue(send);
    15                 await Task.Delay(200);
    16             }
    17         }
    18 
    19  /// <summary>
    20         /// 写寄存器操作
    21         /// </summary>
    22         /// <returns></returns>
    23         static async Task Write()
    24         {
    25             for (short i = 0; i < 10; i++)
    26             {
    27                 var index = BitConverter.GetBytes(i);
    28 
    29                 for (short j = 0; j < 10; j++)
    30                 {
    31                     var data = BitConverter.GetBytes(j);
    32                     byte[] send = new byte[] {
    33                         0x01,0x06,index[1],index[0],data[1],data[0]
    34                         };
    35                     Enqueue(send);
    36                     await Task.Delay(1000);
    37                 };
    38             }
    39         }
    40 
    41  /// <summary>
    42         /// 添加进队列
    43         /// </summary>
    44         /// <param name="send"></param>
    45         private static void Enqueue(byte[] send)
    46         {
    47              cmdList.Enqueue(send);
    49         }

    取出,并发送

        /// <summary>
            /// 从队列里取出内容并发送
            /// </summary>
            /// <returns></returns>
            static async Task Loop()
            {
                while (true)
                {
                    if (cmdList.Count > 0)
                    { 
                      var send = cmdList.Dequeue;
                        
                        //发送操作
                        var sendByte = crc16(send);
                        sp.Write(sendByte, 0, sendByte.Length);
                await Task.Delay(50);//加上这句似乎就可以正常运行了。。。。之前没加才有了接下来的内容。 }
    else { await Task.Delay(50); } } }

    然后Task.Run(async()=>{Loop();});似乎就可以了,由于每次发送完我没加Delay(),导致我在slave工具中看到黏包的现象

    一次收到2条C#发来的命令,一度怀疑发送的时候,命令就是连一起发的,队列里面的内容被2个线程同时添加了命令,带着这个疑问,问了老朋友,结果他发来一个地址,讲的《C#中线程安全集合篇》。

    线程安全的集合类,

    .net framework4新引入的五个专门支持多线程添加和删除操作而设计的集合类型。不同于以前版本的中集合类型中的SyncRoot属性 以及 Synchronized()方法,这些新类型使用了高效的锁定和免锁定同步机制

    ConcurrentQueue(T)
    ConcurrentStack(T)
    ConcurrentDictionary(TKey, TValue)
    ConcurrentBag(T)
    BlockingCollection(T)

    后来改了代码,使用的是BlockingCollection(T)

     1 static BlockingCollection<byte[]> cmdListBlocking = new BlockingCollection<byte[]>();
     2 
     3         /// <summary>
     4         /// 添加进队列
     5         /// </summary>
     6         /// <param name="send"></param>
     7         private static void Enqueue(byte[] send)
     8         {
     9             cmdListBlocking.Add(send);
    10         }
    11 
    12       //取出来,并发送
    13      Task.Factory.StartNew(async () =>
    14             {
    15                 foreach (var vale in cmdListBlocking.GetConsumingEnumerable())
    16                 {
    17                     //发送操作
    18                     var sendByte = crc16(vale);
    19                     sp.Write(sendByte, 0, sendByte.Length);
    20                     await Task.Delay(50);//依然要加Delay()...
    21                 }
    22             });

    不加Task.Delay(50)还是会出现黏包的命令,可能是串口发送数据缓冲区?如果同时进行2个 Task.Run(async()=> { await Write(); });,不加Delay(),是写不上数据的,哪位大神告诉是什么原因呢,对串口发送数据机制还是不够理解。。。

    做个备份

    参考:https://www.cnblogs.com/chengxiaohui/articles/5672768.html  C# 4.0 之线程安全集合篇

     

     

  • 相关阅读:
    c# winform DataGridView导出数据到Excel中,可以导出当前页和全部数据
    水晶报表动态加载图片(签名)
    第1章 开启Threejs之旅(一)
    Python中@contextmanager的用法
    Systemd
    Python中with的用法
    systemctl的配置和使用
    /*CS5460_Note_1*/
    Just try the code
    MCP2515无BUG版本驱动(C文件)
  • 原文地址:https://www.cnblogs.com/guzhongx/p/8400180.html
Copyright © 2020-2023  润新知