【串列埠通訊】--執行緒應用(1)
一、前言:
關於串列埠通訊中的執行緒問題,本來是早就想總結一下的。但是在這兩個星期的學習過程中,發現自己原來的理解還是有很多的不全面的地方。通過兩個月的學習,自己對這塊的認識還是有了很大的提升,今天我就拿出來給大家一起分享一下。當然對於初次接觸執行緒的人來說,可能會不太容易接受,今天就先說點簡單的入門。
二、背景介紹:
首先說說串列埠下的多執行緒,這個其實是有兩種情況的。
- 就是你面對的是多個串列埠,也就是你所要實現的是同一臺電腦上接多個串列埠,然後,他們可能同時發資料、接收資料。這個聽起來好像必須使用多執行緒,但其實多串列埠跟執行緒沒有直接的關係,如果你要做一個軟體針對多個串列埠裝置的話需要多個計算機的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事件不能控制資料的接收是否完全,這是也有他的解決方法?你需要去考慮?最後開啟的子執行緒中需要處理接收到的資料,如果一旦在處理過程中出現錯誤,主執行緒是不能捕捉到的我們如何處理?
這些在我的後續部落格中都會有涉及,敬請關注。