none
VB2008 使用多執行緒讀取同一個字串轉換模組疑問 RRS feed

  • 問題

  • 請教各位大大

    小弟利用PC上2個RS232的埠 對不同PLC連線 數據的傳輸目前是OK的!但有疑問是在於轉碼的部分:

    多緒宣告:

    Private Sub Panel_Check()
    
            For Run_Cunt As Integer = 0 To 15
                If PLC_Alive(Run_Cunt) = True Then
                    Select Case Run_Cunt
                        Case 0
                            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf READ_PLC1), myResetEvent)
                        Case 1
                            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf READ_PLC2), myResetEvent)
            
                  End Select
                End If
            Next
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf PLC_ALARM_Read), myResetEvent)
        End Sub

    連線程式(PLC1與PLC均同):

     Private Sub READ_PLC1()
    
            If PLC_Alive(0) = True Then
                Dim PortNum As Integer
                For ComPort_Check As Integer = 0 To myDataBuffer.PLC_Table.Rows.Count - 1
                    If myDataBuffer.PLC_Table.Rows(ComPort_Check).Item(1) = 1 Then
                        PortNum = myDataBuffer.PLC_Table.Rows(ComPort_Check).Item(2)
                    End If
                Next
    
                Try
                    Do
                        If Form_Close = False Then
                            Dim PLC_FINE As String = "PLC1_FINE"
                            Dim FINE_CUNT As Integer = CInt(IniReadValue(PLC_FINE, "FINE_CUNT", 0))
                            Static Wait_time_Base As Integer
                            For i As Integer = 0 To FINE_CUNT
                                If i < FINE_CUNT Then
                                    Select Case i
                                        Case i
                                            Dim Read_Command As String = "Read" & (i + 1)
                                            Dim SXD_Hander As String = IniReadValue(PLC_FINE, Read_Command, 0)
                                            Dim Send_SXD As String = Chr(5) + SXD_Hander + SUM(SXD_Hander) + Chr(13)
                                            Dim READ_ITEM As String = IniReadValue(PLC_FINE, Read_Command, 1)
                                            Dim Wait_Time As Integer = CInt(IniReadValue(PLC_FINE, Read_Command, 2))
                                            myDataBuffer.Comport(PortNum).WriteLine(Send_SXD)
                                            System.Threading.Thread.Sleep(Wait_Time + Wait_time_Base)
                                            Speed_Display(1, PortNum, (Wait_Time + Wait_time_Base) / 1000)
                                            If myDataBuffer.Comport(PortNum).BytesToRead > 0 Then
                                                Try
                                                    Dim Read_Byte(264) As Byte
                                                    ReDim Read_Byte(myDataBuffer.Comport(PortNum).BytesToRead - 1)
                                                    Dim length As Int32 = myDataBuffer.Comport(PortNum).Read(Read_Byte, 0, myDataBuffer.Comport(PortNum).BytesToRead)
                                                    Test = Read_Byte
                                                    If Read_Byte(length - 3) = 3 Then
                                                        PLC_Data_Conver(Read_Byte, READ_ITEM, 1)
                                                     Else
                                                        Wait_time_Base = Wait_time_Base + 10
                                                        System.Threading.Thread.Sleep(100)
                                                        Dim Readstr As String = myDataBuffer.Comport(PortNum).ReadExisting
                                                        Exit For
                                                    End If
                                                Catch ex As Exception
                                                    Wait_time_Base = Wait_time_Base + 10
                                                    Dim Readstr As String = myDataBuffer.Comport(PortNum).ReadExisting
                                                End Try
                                            End If
                                    End Select
                                Else
                                    ChooseThreads(1)
                                End If
                                System.Threading.Thread.Sleep(50)
                            Next
                        Else
                            Exit Do
                        End If
                    Loop
                Catch ex As Exception
                    System.Threading.Thread.Sleep(500)
                    myDataBuffer.Comport(PortNum).Close()
                End Try
            End If
          
        End Sub

    解碼:

    '由ITEM 判別該放入哪一台PLC的資料區
    Public Sub PLC_Data_Conver(ByVal PLC_Byte() As Byte, ByVal PLCITEM As String, ByVal item As Integer)
            Dim PLC_Case As String = Mid(PLCITEM, 1, 1)
            Dim PLC_DM As Integer = CInt(Mid(PLCITEM, 2, 4))
            Select Case PLC_Case
                Case "D"
                    For i2 As Integer = 0 To 63
                        Dim DM_item As Integer = 5 + (i2 * 4)
                        myDataBuffer.D_Table.Rows(PLC_DM + i2).Item(item) = System.Convert.ToChar(PLC_Byte(DM_item)) + System.Convert.ToChar(PLC_Byte(DM_item + 1)) + System.Convert.ToChar(PLC_Byte(DM_item + 2)) + System.Convert.ToChar(PLC_Byte(DM_item + 3))
                    Next
                Case "M"
                    Try
                        Dim l As Integer = (PLC_Byte.Length - 8) / 4
                        Dim buffer(l) As String
                        For i2 As Integer = 0 To l - 1
                            Dim DM_item As Integer = 5 + (i2 * 4)
                            For i3 As Integer = 0 To 3
                                Dim M As Integer = (i2 * 16) + (i3 * 4)
                                Dim Read_Str As String = System.Convert.ToChar(PLC_Byte(DM_item + (3 - i3)))
                                Bit_Conver(Read_Str, item, M)
                            Next
                        Next
                    Catch ex As Exception
                        MsgBox(ex.Message, MsgBoxStyle.SystemModal, "Error")
                    End Try
                Case "X"
                    Try
                        Dim l As Integer = PLC_Byte.Length - 8
                        For i As Integer = 0 To l - 1
                            Dim Value As String = System.Convert.ToChar(PLC_Byte(5 + i))
                            myDataBuffer.X_Table.Rows(PLC_DM + i).Item(item) = Value
                        Next
                    Catch ex As Exception
                        MsgBox(ex.Message, MsgBoxStyle.SystemModal, "Error")
                    End Try
                Case "Y"
                    Try
                        Dim l As Integer = PLC_Byte.Length - 8
                        For i As Integer = 0 To l - 1
                            Dim Value As String = System.Convert.ToChar(PLC_Byte(5 + i))
                            myDataBuffer.Y_Table.Rows(PLC_DM + i).Item(item) = Value
                        Next
                    Catch ex As Exception
                        MsgBox(ex.Message, MsgBoxStyle.SystemModal, "Error")
                    End Try
                Case "C"
                    Try
                        Dim l As Integer = PLC_Byte.Length - 8
                        For i As Integer = 0 To l - 1
                            Dim Value As String = System.Convert.ToChar(PLC_Byte(5 + i))
                            myDataBuffer.C_Table.Rows(PLC_DM + i).Item(item) = Value
                        Next
                    Catch ex As Exception
                        MsgBox(ex.Message, MsgBoxStyle.SystemModal, "Error")
                    End Try
                Case "T"
                    Try
                        Dim l As Integer = PLC_Byte.Length - 8
    
                        For i As Integer = 0 To l - 1
                            Dim Value As String = System.Convert.ToChar(PLC_Byte(5 + i))
                            myDataBuffer.T_Table.Rows(PLC_DM + i).Item(item) = Value
                        Next
                    Catch ex As Exception
                        MsgBox(ex.Message, MsgBoxStyle.SystemModal, "Error")
                    End Try
    
            End Select
        End Sub

    Public Sub Bit_Conver(ByVal Value As String, ByVal PLC_ITEM As Integer, ByVal PLC_DM As Integer)
    
            Dim ax As String
            Select Case Value
                Case "0" : ax = "0000"
                Case "1" : ax = "0001"
                Case "2" : ax = "0010"
                Case "3" : ax = "0011"
                Case "4" : ax = "0100"
                Case "5" : ax = "0101"
                Case "6" : ax = "0110"
                Case "7" : ax = "0111"
                Case "8" : ax = "1000"
                Case "9" : ax = "1001"
                Case "A" : ax = "1010"
                Case "B" : ax = "1011"
                Case "C" : ax = "1100"
                Case "D" : ax = "1101"
                Case "E" : ax = "1110"
                Case "F" : ax = "1111"
            End Select
    
            For i As Integer = 1 To 4
                Dim W As Boolean = Mid(ax, (5 - i), 1)
                Dim x As Integer = myDataBuffer.M_Table.Columns.Count
                Dim y As Integer = myDataBuffer.M_Table.Rows.Count
                Try
                    Dim Rows_Cunt As Integer = PLC_DM + (i - 1)
                    myDataBuffer.M_Table.Rows(PLC_DM + (i - 1)).Item(PLC_ITEM) = W
                Catch ex As Exception
                    MsgBox(ex.Message, MsgBoxStyle.SystemModal, "BitConver")
                End Try
    
            Next
    
        End Sub
    

    小弟的疑問是目前在BitConver對"M"作解析時,偶而會出現超出宣告陣列 是否2個 ThreadPool的執行緒會有衝突?

     


    新手上路

    2012年3月27日 上午 08:56

解答

  • [SyncLock 陳述式]

    [Managed 執行緒處理的最佳實施方針]


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    • 已標示為解答 eblue 2012年3月27日 下午 03:13
    2012年3月27日 上午 11:57
    版主

所有回覆

  • 把轉換資料做成另一個類別, 在不同執行緒利用此類別產生新的執行個體做轉換. 避免兩個以上Thread同時存取相同記憶體區塊.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2012年3月27日 上午 09:01
    版主
  • 所以我將轉換資料的那2段程式碼 放在RS232的類別中

    每次轉檔時都產生一個新的執行體嗎?像這樣

     If Read_Byte(length - 3) = 3 Then
                                                        Dim PLC1 As New RS232_Base
                                                        PLC1.PLC_Data_Conver(Read_Byte, READ_ITEM, 1)
                                                    Else
                                                        Wait_time_Base = Wait_time_Base + 10
                                                        System.Threading.Thread.Sleep(100)
                                                        Dim Readstr As String = myDataBuffer.Comport(PortNum).ReadExisting
                                                        Exit For
                                                    End If

    所以說 我在程式載入時便在模組中宣告myDataBuffer AS New RS232_Base

    但我卻有3個執行緒會對myDataBuffer.M_Table作讀跟寫!是否也有可能發生同時讀取相同記憶體區塊的機率

    因為另一個監控異常狀態執行緒也會偶發出現類似的情況

    這是第3個執行緒:  ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf PLC_ALARM_Read), myResetEvent)

      Private Sub PLC_ALARM_Read()
            Do
                '取得元件類別
                Try
                    For PLC_Cunt As Integer = 1 To 16
                        If PLC_Alive(PLC_Cunt - 1) = True Then
                            Dim Read_PLC As String = "PLC" & PLC_Cunt & "_ALARM"
                            Dim ALARM_CUNT As Integer = IniReadValue(Read_PLC, "ALARM_CUNT", 0)
                            For i As Integer = 1 To ALARM_CUNT
                                Dim Read_DM As String = "ALARM" & i
                                Dim Alarm_Item As String = Mid(IniReadValue(Read_PLC, Read_DM, 1), 1, 1)
                                Dim Alarm_NO As Integer = CInt(Mid(IniReadValue(Read_PLC, Read_DM, 1), 2, 4))
                                Select Case Alarm_Item
                                    Case "X"
                                        Dim buffer As String = myDataBuffer.X_Table.Rows(Alarm_NO).Item(PLC_Cunt)
                                        myDataBuffer.ALARM_Stsate_Table.Rows(i).Item(PLC_Cunt) = buffer
                                    Case "Y"
                                        Dim buffer As String = myDataBuffer.Y_Table.Rows(Alarm_NO).Item(PLC_Cunt)
                                        myDataBuffer.ALARM_Stsate_Table.Rows(i).Item(PLC_Cunt) = buffer
                                    Case "M"
                                        Dim buffer As String = myDataBuffer.M_Table.Rows(Alarm_NO).Item(PLC_Cunt)
                                        myDataBuffer.ALARM_Stsate_Table.Rows(i).Item(PLC_Cunt) = buffer
                                    Case "T"
                                        Dim buffer As String = myDataBuffer.T_Table.Rows(Alarm_NO).Item(PLC_Cunt)
                                        myDataBuffer.ALARM_Stsate_Table.Rows(i).Item(PLC_Cunt) = buffer
                                    Case "C"
                                        Dim buffer As String = myDataBuffer.C_Table.Rows(Alarm_NO).Item(PLC_Cunt)
                                        myDataBuffer.ALARM_Stsate_Table.Rows(i).Item(PLC_Cunt) = buffer
                                End Select
                            Next
                        End If
                    Next
                Catch ex As Exception
                    MsgBox(ex.Message, MsgBoxStyle.SystemModal, "PLC_ALARM_READ")
                End Try
                PLC_ALARM_Watch()
            Loop Until Form_Close = True
        End Sub


    新手上路

    2012年3月27日 上午 09:40
  • [SyncLock 陳述式]

    [Managed 執行緒處理的最佳實施方針]


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    • 已標示為解答 eblue 2012年3月27日 下午 03:13
    2012年3月27日 上午 11:57
    版主
  • Bill大您給的資料我看了 以這個SyncLock陳述式來看..小弟不是很理解 我是這做出以下的測試

    Imports System.Threading
    Public Class Form1
        Dim thisLock As New Object
        Dim myResetEvent As New AutoResetEvent(False)
        Dim r As New Random()
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            Table_Make()
            Me.DataGridView1.DataSource = MY_Base.M_Table
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Work1), myResetEvent)
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf work2), myResetEvent)
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf M_Color), myResetEvent)
        End Sub
        Private Sub Work1()
            Do
                Dim i As Integer
                i += 1
                If i = 21 Then
                    i = 0
                End If
                Dim str As String = (r.Next(1, 100)) * 1.5
    
                D_Display(i, str, 1)
            Loop
        End Sub
        Private Sub work2()
            Do
                Dim i As Integer
                i += 1
                If i = 21 Then
                    i = 0
                End If
                Dim str As String = (r.Next(1, 100)) * 0.9
    
                D_Display(i, str, 2)
            Loop
        End Sub
        Private Sub D_Display(ByVal item As Integer, ByVal str As String, ByVal c As Integer)
            SyncLock thisLock
                MY_Base.M_Table.Rows(item).Item(c) = str
            End SyncLock
        End Sub
    
        Private Sub M_Color()
            Do
                For i As Integer = 0 To 20
                    If MY_Base.M_Table.Rows(i).Item(1) > MY_Base.M_Table.Rows(i).Item(2) Then
                        COLOR_Display(Color.Blue)
                    Else
                        COLOR_Display(Color.Red)
                    End If
                    System.Threading.Thread.Sleep(500)
                Next
            Loop
        End Sub
        Delegate Sub D_Color(ByVal C_Color As Color)
        Private Sub COLOR_Display(ByVal D_Color As Color)
            If Me.InvokeRequired Then
                Dim d As New D_Color(AddressOf COLOR_Display)
                Me.Invoke(d, New Object() {D_Color})
            Else
                Panel1.BackColor = D_Color
            End If
        End Sub
    End Class

    但是會出現這個異常訊息


    新手上路

    ***

    這邊加個Sleep感覺就不會有錯誤,DataGridview元件也可正常捲動及選取

       Private Sub D_Display(ByVal item As Integer, ByVal str As String, ByVal c As Integer)
            Static v As Integer
            SyncLock thisLock
                v = c + v
                MY_Base.M_Table.Rows(item).Item(c) = str & "   " & Thread.CurrentThread.GetHashCode()
                MY_Base.M_Table.Rows(item).Item(8) = v
                If v > 100 Then
                    v = 0
                End If
                System.Threading.Thread.Sleep(10)
            End SyncLock
        End Sub

    至於那個錯誤的訊息方塊 還真不知是如何造成的?
    • 已編輯 eblue 2012年3月27日 下午 02:03
    2012年3月27日 下午 12:52
  • 出現這種問題, 你應該要先檢查當時的值的變化. 查看看問題出在哪, 我記得你程式也寫滿多年了, 怎麼追蹤數值變化應該有點經驗了吧.

    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2012年3月27日 下午 02:04
    版主
  • 我有試著看值的變化 因為Row的數量 我在建立時建立時就宣告21行,所以從Rows(0)開始填至ROW(20),故當i=21時我就宣告i=0!應當不會超過!所以才納悶~


    新手上路

    2012年3月27日 下午 03:12
  • 我有試著看值的變化 因為Row的數量 我在建立時建立時就宣告21行,所以從Rows(0)開始填至ROW(20),故當i=21時我就宣告i=0!應當不會超過!所以才納悶~


    新手上路

    我一直執著在 Work1及Work2的工作裡!但實際出現問題的是在M_Color的工作區塊中!由於再現性太低!昨夜RUN了一夜早上才出現!

    另問多緒執行不是可以同時讀嗎?所以我一直覺得讀應該是OK!是寫數值變更的程式區塊出問題所以才一直沒觀察到!


    新手上路


    • 已編輯 eblue 2012年3月28日 上午 01:27
    2012年3月28日 上午 01:25
  • 你的意思是說, 那個索引在陣列界限之外的問題是出在這個區塊嗎?

    Private Sub M_Color()
            Do
                For i As Integer = 0 To 20
                    If MY_Base.M_Table.Rows(i).Item(1) > MY_Base.M_Table.Rows(i).Item(2) Then
                        COLOR_Display(Color.Blue)
                    Else
                        COLOR_Display(Color.Red)
                    End If
                    System.Threading.Thread.Sleep(500)
                Next
            Loop
        End Sub

    那M_Color 是由誰呼叫的 ?


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2012年3月28日 上午 04:03
    版主
  • M_Color 是一開始就執行的

    ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf M_Color), myResetEvent)
    是剛好比對到的欄位,Work1 或Work2的執行緒正在變更他嗎?


    新手上路

    2012年3月28日 上午 06:57
  • 那有誰會去更動 MY_Base.M_Table ?

    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。

    2012年3月28日 上午 08:41
    版主
  • Work1 跟 Work2 均會對 MY_Base.M_Table 中的值作變更!就是說雖然我只是取值出來比對 仍應注意 MY_Base.M_Table是否被占據?

    新手上路

    2012年3月28日 上午 09:18