串口SerialPort 死锁问题

转载

微软SerialPort秘籍,SerialPort为什么死锁程序的分析 作者:天涯 来源:中国自学编程网 发布日期:1232178634

      既然是秘籍,显然是写一些大家不常找到的,MSDN里遗漏提示大家注意的东西。 用过.net 2.0中,自带SerialPort的人,大多都遇到过。莫名其妙的执行Close的时候会死掉的问题。而Wince,mobile下,甚至Write,WriteLine的时候也会死锁。这和串口底层驱动关。    

1.Close的时候死机问题 我只是猜测,Close执行的操作是调用2个API:

 SetCommMask(m_hComPort,0);

WaitForSingleObject(m_pThread->m_hThread,INFINITE);

 m_pThread=NULL;

i(m_hComPort=INVALID_HANDLE_VALUE) CloseHandle(m_hComPort);

 这里的话,SetCommMask操作的时候。如果你的DeviceReceived事件没执行完,里面还有更新界面的操作。就会监听线程在等待,执行到界面操作,界面又要求关闭监听线程然后关闭串口。死锁了。 [解决办法] 一个正确不会死机的串口程序(主要是DataReceived事件里如果要调用ui来显示数据或分析结果的时候。ui的效率不确定,ui可能快。可能慢,可能用户某个操作阻塞中,会导致DataReceived事件执行很长时间,不论如何,这个时间我们无法预料)。 应该在关闭之前执行这句

while (Environment.TickCount - i < 2000 && _isReceiving)

Application.DoEvents();

 2.WriteLine,Write死锁 串口必然会读写超时,你无法预测你软件运行的硬件都足够快,串口驱动都效率很高。并且读写超时是正常现象。但是微软的SerialPort类默认的读写超时都是-1,无限等待,如果串口驱动有效率陷阱,超时了。Write和WriteLine就导致软件死锁无法继续使用了。 [解决办法] 设置读写超时为1000毫秒 3.不知道有人注意过没,WriteLine好像和Write效果一样?不是一个错误,而是微软一个很奇怪的设计,他允许设置换行符,默认是"",你需要自己设置NewLine属性为"/r/n"。否则Write和WriteLine是一样的。 -------------------------------------------------------------------------------- 下面演示一个标准的,不会软件死锁的基于SerialPort的例子:

 using System;

 using System.Drawing;

using System.Text;

 using System.Windows.Forms;

using System.IO.Ports;

using System.Collections.Generic;

namespace SerialSample

{

 public partial class FormSerialSample : Form

{

 //Fields SerialPort _comm = new SerialPort("COM1", 19200);

 bool _isReceiving = false; //wince下4096字节为一页,一次分配一页内存可能会快。但不知道托管内存是否也如此

 List buf = new List(4096);

 //Construct public FormSerialSample() { InitializeComponent();

 }

bool Open()

{

_comm.WriteTimeout = 1000;//写超时,如果底层串口驱动效率问题,能有效的避免死锁

_comm.ReadTimeout = 1000;//读超时,同上

_comm.NewLine = "/r/n";//新行的文本,用于WriteLine方法中由系统附加在text后

_comm.DataReceived += OnComm;//注册事件

_comm.Open();//打开串口

return _comm.IsOpen;//返回打开结果

}

void Close()

{

//这里需要允许OnComm方法执行完。如果不这么做。可能会

//先由某个地方执行到了Close,然后监听线程触发,调用了OnComm

 //OnComm执行完缓存,执行this.Invoke。不知道Invoke原理。可能是

 //消息机制?那么你如果正在这个函数里,不释放的时候,你就不会继续

//处理消息,所以我们要关闭之前,手工的调用消息处理函数

_comm.DataReceived -= OnComm;//反注册事件,避免下次再执行进来。

//最大延迟2秒,并检测到OnComm退出则退出,处理系统消息队列中的消息

int i = Environment.TickCount;

while (Environment.TickCount - i < 2000 && _isReceiving) Application.DoEvents();

_comm.Close();//现在没有死锁了,关闭串口

}

void OnComm(object sender, SerialDataReceivedEventArgs e)

 {

 _isReceiving = true;//开始读 //缓存你的数据,注意,最好在Invoke之外缓存数据,只有更新UI的才需要

Invoke int count = _comm.BytesToRead;

if (count < 1)

return;

 byte[] tmpBuf = new byte[count];

 _comm.Read(tmpBuf, 0, count); //连续3k数据没分析,应该没啥用了。

//避免数据太多分析时候时间长导致效率明显下降

if (buf.Count > 3000)

buf.Clear();

 buf.AddRange(tmpBuf); //分析数据

//自己实现

//匿名委托,用于this.Invoke调用。

EventHandler delUpdate = delegate

{

//执行你的更新ui操作

};

this.Invoke(delUpdate); _isReceiving = false;

//结束读

}

 }

 }

串口是没啥技术含量。但是这个微软的SerialPort实在给不少人带来了麻烦,死机是绝大多数人遇到的,我以前是MSDN,微软的技术论坛,CSDN,GOOGLE,Baidu都搜索了。甚至发了邮件给微软的wince开发组,没有任何答案。不求您觉得我写的有什么价值,但希望对有同样困惑的朋友。能搜索到此文章时候,解决一个隐含的问题

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值