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

2018-02-03 10:18:49来源:http://www.cnblogs.com/guzhongx/p/8400180.html作者:博客园_WaitingEver人点击

分享

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


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



1 public void Read(){
2 while(true){
3lock(this)
4SerialPort.Write(读取的命令内容1);
5Thread.sleep(500);
6lock(this)
7 SerialPort.Write(读取的命令内容2);
8Thread.sleep(500);
9//..可能还有....
10 }
11
12 }
13
14 public void Write1(){
15 lock(this)
16SerialPort.Write(写入的命令内容);
17 }
18
19 public void Write2(){
20 lock(this)
21SerialPort.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 {
120x01,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 {
47cmdList.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 之线程安全集合篇


最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台