一、前言:
关于串口通信中的线程问题,本来是早就想总结一下的。但是在这两个星期的学习过程中,发现自己原来的理解还是有很多的不全面的地方。通过两个月的学习,自己对这块的认识还是有了很大的提升,今天我就拿出来给大家一起分享一下。当然对于初次接触线程的人来说,可能会不太容易接受,今天就先说点简单的入门。
二、背景介绍:
首先说说串口下的多线程,这个其实是有两种情况的。
- 就是你面对的是多个串口,也就是你所要实现的是同一台电脑上接多个串口,然后,他们可能同时发数据、接收数据。这个听起来好像必须使用多线程,但其实多串口跟线程没有直接的关系,如果你要做一个软件针对多个串口设备的话需要多个计算机的COM口,这样软件中可以创建多个serialport实例,每个serialport针对一个COM口操作,每个serialport的DataReceived事件可以用一个处理方法也可以分别用处理方法。
- 就是串口的接收事件,也就是监听事件,DataReceived本身也是一个线程,但是如果频繁接收数据,或者数据量最大的时候最好不建议直接在DataReceived事件中处理,这样可能导致前一帧数据还没有处理,下一帧数据到来的时候丢失了。处理的时候可以用多个线程来处理,比如开一个线程专门处理接收到的数据,一个线程专门来发送数据。
三、线程基本知识:
说这么多,大家应该是迫不及待的想了解一下线程的知识了。首先我说一下线程的基本知识:
线程是程序执行的基本单位,一个进程可以由一个或多个线程组成。利用多个线程同时处理的编程理念就是多线程处理,多线程并不是真正多个线程同时占有CPU,任一时刻只能有一个线程占用CPU,只是同时争夺CPU更频繁,因为每个线程占用CPU的时间都非常短,宏观上感觉是同时进行,线程的优先级也并不是让该进程先执行,而是CPU分配给该线程的时间更多而已。
1、Thread类最常用的方法:
Start:开启一个线程。
Abort:终止线程。
Sleep:暂停线程。
Jone:当NewThread调用Join方法的时候,MainThread就被停止执行,直到NewThread线程执行完毕
以上的4个方法中前三个都很容易理解。最后的jone方法需要给大家解释一下。解释这个方法之前先引入两个概念,“主线程”和“子线程”。
如果程序在一次方法调用的过程中,创建了一个新的线程,则把程序按顺序执行看做是一个线程——“主线程”,新开启的线程——“子线程”。
2、举例说明Jone方法:
Imports System.Threading
Module Module1
Private Sub ThreadFuncOne()
For i = 0 To 9
Console.WriteLine(Thread.CurrentThread.Name + " i = " + i.ToString)
Next
Console.WriteLine(Thread.CurrentThread.Name + " has finished")
End Sub
Sub Main()
Thread.CurrentThread.Name = "MainThread"
Dim newThread As Thread = New Thread(AddressOf ThreadFuncOne)
newThread.Name = "NewThread"
For j = 0 To 20
If j = 10 Then
newThread.Start()
newThread.Join()
Else
Console.WriteLine(Thread.CurrentThread.Name + " j = " + j.ToString)
End If
Next
Console.ReadLine()
End Sub
End Module
3、运行结果:
从运行结果可以看出:从测试中我们可以很清楚的看到MainThread在NewThread.Join被调用后被阻塞即进入等待状态,直到NewThread 执行完毕才继续执行。
四、在串口通信中的应用:
在串口通信中,我们除了用其已经封装好的DataReceived方法接收数据我们完全可以在调用发送命令后手动开启一个线程用来接收串口返回的数据,尤其是对于已经定义好通信协议的实际应用中,同一种硬件返回的数据往往是具有相同的长度的。我们只需要每次强制的从串口中读取一定长度的数据,然后对其进行解析,这样就很好的解决了本文上面说说的DataReceived事件所具有的问题(前一帧数据还没有处理,下一帧数据到来的时候丢失了!)
1、代码:
''' <summary>
''' 向串口发送数据方法
''' </summary>
''' <param name="mCommand">发送的具体命令</param>
''' <remarks></remarks>
Public Sub Send(ByVal mCommand As Stringl)
Try
Dim readThread As Thread = New Thread(AddressOf Read)
_serialPort.DiscardOutBuffer()
_serialPort.WriteLine(mCommand )
readThread.Start() '开始读线程
readThread.Join()
Catch ex As Exception
Throw ex
End Try
End Sub
''' <summary>
'''接收串口数据方法
''' </summary>
''' <remarks></remarks>
Private Sub Read()
Try
_serialPort.DiscardInBuffer()
Dim str As String = ""
Dim buf() As Byte
For i = 0 To 10'假设接收到的数据每次应为11个
Dim d As Integer
d = _serialPort.ReadByte'从串口中读取一个字节的数据,如果设置了读取超时时间,在规定的时间未读到数据会触发超时错误。
str += Convert.ToString(d, 16).PadLeft(2, "0")
Next
buf = GetByte(str)
'根据协议,处理收到的数据
Catch ex As Exception
Throw ex
End Try
End Sub
2、这样做的优点:
可以实现发送命令时只有这个命令对应的返回数据全部接收完全才开始发送另一个命令,不会出现错误的数据。
五、最后要说的:
这样做的缺点,一般情况下我们的系统所面对的硬件类型不可能是一种,如果有多种命令。很可能返回的数据长度不一样,所以我们的read方法是一定不能写死的,当然我们完全可以根据不同的命令类型来判断我们需要读的数据的大小,但是总是有些时候。是不能根据命令类型判断的,这个时候我们应该怎么办呢?当然我不是说DataReceived事件不能控制数据的接收是否完全,这是也有他的解决方法?你需要去考虑?最后开启的子线程中需要处理接收到的数据,如果一旦在处理过程中出现错误,主线程是不能捕捉到的我们如何处理?
这些在我的后续博客中都会有涉及,敬请关注。
转载请注明出处!http://blog.csdn.net/hy6688_/article/details/18057167