C# SerialPort串口通信 线程问题

xuxingyuan564335 2017-11-16 12:35:43
请问下大虾们,小弟遇到一个棘手的问题,我们有个项目是获取串口读卡器数据,然后进行操作,每次刷卡,串口事件就获取到数据,但是 现在发现 void sp_DataReceived(object sender, SerialDataReceivedEventArgs e) 此串口事件方法是每次进来就自动生成一个线程在执行,而且此方法执行完后此线程又没有进行关闭,所以很苦恼,如果一个小时内刷卡达到上千次,岂不是串口事件就要生成1000个线程,太恐怖了;
请教各位大虾 有没有遇到这种情况呢? 非常感谢
...全文
640 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
我给你打一个比方吧。比如说有一个公司某月收到1千件快递,原本是这个公司的前台小姑娘在前台随时接收货物,现在这个小姑娘每接收一次快递就去厕所拉屎(故意Sleep)25天,这个小姑娘是不是屎太多了?这就造成快递人员有9百多次徒劳地跑到公司前台来,这就造成了快递系统记录的压单数量太多。 你贴的代码属于这种“不称职的前台小姑娘”的思路。一个真正合格的异步通讯程序,根本没有什么 Sleep 代码。先从删除 Sleep 代码入手学习吧。
  • 打赏
  • 举报
回复
你所贴出的所谓的线程列表,那只能说明 vs 给你贴出了一堆垃圾线程(池中的线程),并不能证明这些线程还在并发执行着你的代码(你的过程还没有执行完毕)。你应该自己测试一下,是否真的有所谓并发执行你的 sp_DataReceived 的情况。如果有,那么我们就别用 .net 的 SerialPort 类了,它太不靠谱了,不用纠结什么额外的东西。 人家列表中贴出的那么多线程(池中已经废弃、准备重用的线程),跟你的 sp_DataReceived 过程有什么关系?你怎么就知道自己的 sp_DataReceived 过程会被 1000 个县城并发执行?
  • 打赏
  • 举报
回复
在 sp_DataReceived 事件处理过程被触发执行时,通常是宿主的系统机制是不可能并发触发多个 DataReceived 事件的,通常是前一个事件处理完毕之后才出发下一个事件,以避免乱序。所以你的代码严重阻塞了底层通讯机制,整个系统可能会被卡死。
易2017 2017-11-21
  • 打赏
  • 举报
回复
引用 13 楼 ericwuhk 的回复:
串口事件跑了一半 代码还没跑完又触发事件了 估计是这个问题

bool isAllowedflag=true;
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    if(isAllowedflag)
    {
      isAllowedflag=false;
      ...///你的代码
      isAllowedflag=true;
    }
}
某些情况下这个方法不错,get
  • 打赏
  • 举报
回复
引用 7 楼 xuxingyuan564335 的回复:
[quote=引用 5 楼 易2017的回复:]int cnt = sp.Read(ReceiveBuffer, 0, 32); //延时20ms,等待系统自动将其它所有数据都接收完再一起读 Thread.Sleep(35); cnt += sp.Read(ReceiveBuffer, cnt, 32); 你这写法清新脱俗啊
这个写法是厂家给我的 按照他们的代码拷贝上去的,我也是第一次弄这个玩意儿[/quote] 基本上可以判断,有 Sleep 的代码就是比较差的。异步接收通讯数据本身就应该是考虑到“分包、粘包”的,它判断是否接收完毕完整的消息,如果没有接收完毕就不会去开始解解析处理(也根本不可能正确解析消息)。它对消息的处理是准确和及时的,根本用不着认为加上什么 Sleep 代码。
  • 打赏
  • 举报
回复
哈哈,P哥的比喻999啊 你可以写个全局的List<byte> ,然后在SerialPort.DataReceived事件方法里,都把数据存进去,判断是否接到尾部了,是尾部了就进行处理,最后清掉List<byte>的数据好进行下一个。。。
易2017 2017-11-21
  • 打赏
  • 举报
回复
P哥简直不要太凶残
Antony_WU_SZ 2017-11-20
  • 打赏
  • 举报
回复
串口事件跑了一半 代码还没跑完又触发事件了 估计是这个问题

bool isAllowedflag=true;
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    if(isAllowedflag)
    {
      isAllowedflag=false;
      ...///你的代码
      isAllowedflag=true;
    }
}
xuxingyuan564335 2017-11-16
  • 打赏
  • 举报
回复
引用 1 楼 u010941149 的回复:
上代码啊上代码
代码已发
xuxingyuan564335 2017-11-16
  • 打赏
  • 举报
回复

/// <summary>
        /// 打开串口
        /// </summary>
        private void openPort()
        {
            try
            {
                if (sp ==null)
                {
                    sp = new SerialPort();
                    sp.PortName = baseClass.scom;
                    sp.BaudRate = 9600;
                    sp.StopBits = StopBits.One;
                    sp.DataReceived += sp_DataReceived;
                }
                else if (!sp.IsOpen)
                {
                    sp.Open();
                }
            }
            catch (Exception)
            {

            }
        }
  #region 串口数据读取

        void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            string message = "";
            byte[] ReceiveBuffer = new byte[100];
            try
            {
                readCardState = false;
                int cnt = sp.Read(ReceiveBuffer, 0, 32);
                //延时20ms,等待系统自动将其它所有数据都接收完再一起读
                Thread.Sleep(35);
                cnt += sp.Read(ReceiveBuffer, cnt, 32);

                //总数据
                byte[] sumData = new byte[cnt];
                //卡数据
                //byte[] byteTemp = new byte[16];
                //卡号
                byte[] byteCarID = new byte[4];

                sumData[0] = ReceiveBuffer[0];
                //去掉数据中多出的一个7F
                for (int i = 1, j = 1; i < cnt; i++)
                {
                    //先判断一下这个为7F时,上一个也是7F的话,就是双个7F,就丢弃
                    if (ReceiveBuffer[i] == 0x7F && ReceiveBuffer[i - 1] == 0x7F) { continue; }
                    sumData[j++] = ReceiveBuffer[i];
                }
                //判断是否成功 0x00 读卡成功
                if (sumData[4] == 0x00)
                {
                    //获取卡号
                    byteCarID[0] = sumData[7];
                    byteCarID[1] = sumData[8];
                    byteCarID[2] = sumData[9];
                    byteCarID[3] = sumData[10];

                    if (sumData.Length == 29)
                    {


                        //获取卡数据
                        for (int i = 11, j = 0; i < 27; i++, j++)
                        {
                            //byteTemp[j] = sumData[i];
                            message += sumData[i].ToString("X2");
                        }
                        message = HexStringToString(message, Encoding.UTF8);

                        //卡号
                        cardnumber = Convert.ToInt64(message);

                        //判断刷卡时间 连续刷卡判断同一张卡N 秒内不重复
                        DataRow[] dr = cardinfoDataTable.Select("CRID='" + cardnumber + "'");

                        if (dr != null && dr.Count() > 0)
                        {
                            if ((Int32.Parse(DateTime.Now.ToString("HHmmss")) - Int32.Parse(dr[0]["SKTIME"].ToString())) > baseClass.CardInfoTime)
                            {
                                //刷卡判断
                                if (PAYBYCARD(cardnumber))
                                {
                                    DataRow drEmployee = dr[0];

                                    drEmployee.BeginEdit();
                                    drEmployee["SKTIME"] = Int32.Parse(DateTime.Now.ToString("HHmmss"));
                                    drEmployee.EndEdit();
                                }
                                else
                                {
                                    Voice.Speak("刷卡失败,请从刷", SpFlags);
                                }

                            }
                            else
                            {

                                Voice.Speak("重复刷卡", SpFlags);
                            }
                        }
                    }

                }
            }
            catch (Exception ex)
            {
                Loger.writeErr_Log("卡号:" + message + "刷卡异常:", ex.ToString());
                Voice.Speak("刷卡异常", SpFlags);
            }

        }

        #endregion

sdfgrtyu 2017-11-16
  • 打赏
  • 举报
回复
上代码啊上代码
易2017 2017-11-16
  • 打赏
  • 举报
回复
易2017 2017-11-16
  • 打赏
  • 举报
回复
http://bbs.csdn.net/topics/392263509
这是我以前发的一个帖子,你可以了解下
  • 打赏
  • 举报
回复
串口事件尽量简短。 void sp_DataReceived(object sender, SerialDataReceivedEventArgs e) { int cnt += sp.Read(ReceiveBuffer, cnt, 32); //然后将读取的数据写入缓冲区 ...... } 另一个线程从缓冲区读取数据并判断。 缓冲区类自己写一个吧。
lescper2011 2017-11-16
  • 打赏
  • 举报
回复
线程池,限制线程,线程读取之后立即退出
ourhouzi 2017-11-16
  • 打赏
  • 举报
回复
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e) 里面的代码都注释掉 跑跑看会不会出现原来的情况
xuxingyuan564335 2017-11-16
  • 打赏
  • 举报
回复
引用 5 楼 易2017的回复:
int cnt = sp.Read(ReceiveBuffer, 0, 32); //延时20ms,等待系统自动将其它所有数据都接收完再一起读 Thread.Sleep(35); cnt += sp.Read(ReceiveBuffer, cnt, 32); 你这写法清新脱俗啊
这个写法是厂家给我的 按照他们的代码拷贝上去的,我也是第一次弄这个玩意儿
sdfgrtyu 2017-11-16
  • 打赏
  • 举报
回复
是不是刷的太快了啊,,,
易2017 2017-11-16
  • 打赏
  • 举报
回复
int cnt = sp.Read(ReceiveBuffer, 0, 32); //延时20ms,等待系统自动将其它所有数据都接收完再一起读 Thread.Sleep(35); cnt += sp.Read(ReceiveBuffer, cnt, 32); 你这写法清新脱俗啊
易2017 2017-11-16
  • 打赏
  • 举报
回复
把你串口里的延时操作去掉,一般情况下串口事件里的代码跑完线程自动关闭,你这种情况可能是串口线程没跑完的时候串口事件又被触发,导致线程不断增加

110,545

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧