Вот еще один пример более конкретно к последовательному порту. Он использует словарь для отслеживания физических ресурсов комманды и их соответствующих блокировок, так что вы можете выполнять асинхронный доступ к различным коммуникационным портам, но синхронизировать доступ к каждому отдельному коммуникационному порту.
Sub Main()
Dim c1 As New CommPortThreadSafe("COM1")
Dim c2 As New CommPortThreadSafe("COM2")
Dim c3 As New CommPortThreadSafe("COM1")
Dim t1 As New Threading.Thread(Sub() c1.Read())
Dim t2 As New Threading.Thread(Sub() c2.Read())
Dim t3 As New Threading.Thread(Sub() c3.Read())
' t1 and t3 can't be in critical region at same time
' t2 will be able to run through critical region
t1.Start()
t2.Start()
t3.Start()
End Sub
Public Class CommPort
Public Property Name As String
Public Function Read() As String
Return "result"
End Function
End Class
Public Class CommPortThreadSafe
Private Shared resourceLocks As New Dictionary(Of String, Object)()
Private Shared comms As New Dictionary(Of String, CommPort)()
Private Shared collectionLock As New Object()
Private commPortName As String
' constructor takes the comm port name
' so the appropriate dictionaries can be set up
Public Sub New(commPortName As String)
SyncLock collectionLock
Me.commPortName = commPortName
If Not comms.ContainsKey(commPortName) Then
Dim c As New CommPort()
Dim o As New Object()
c.Name = commPortName
' configure comm port further etc.
comms.Add(commPortName, c)
resourceLocks.Add(commPortName, o)
End If
End SyncLock
End Sub
Public Function Read()
Dim res As String
SyncLock resourceLocks(Me.commPortName)
res = comms(Me.commPortName).Read()
End SyncLock
Return res
End Function
End Class
Для решения ваших последних изменений:
Темы А будет все объявить порт комм таким же образом. На самом деле это преимущество этого шаблона (похожего на многотонный рисунок), который работает как одноэлементный, когда используется только один коммуникационный порт. Этот код может быть использован во всех потоках:
Dim myCommPort As New CommPortThreadSafe("COM1")
замок внутри чтения будет синхронизировать доступ к COM1, потому что «COM1» (название порта комм) на самом деле ключ к Dictionary<string, object>
используется для блокировки , Поэтому, когда какой-либо поток достигает этого кода, используя тот же ключ, этот регион будет доступен только одному потоку, потому что все они используют один и тот же ключ.
SyncLock resourceLocks(Me.commPortName)
res = comms(Me.commPortName).Read()
End SyncLock
Как вы видели, что строка устанавливается в конструкторе так, пока все нити создают их объект, пролетающий ту же строку в конструктор, все они будут иметь основные косвенные ссылки на то же CommPort
.Конструктор может только создать экземпляр, если имя не существует в словаре:
If Not comms.ContainsKey(commPortName) Then
Dim c As New CommPort()
Вот еще один пример использования только с одним коммуникационного порта:
Sub Main()
Dim ts As New ThreadStart(
Sub()
Dim c As New CommPortThreadSafe("COM1")
For i As Integer = 0 To 99
c.Read()
Next
End Sub)
Dim t1 As New Threading.Thread(ts)
Dim t2 As New Threading.Thread(ts)
Dim t3 As New Threading.Thread(ts)
Dim t4 As New Threading.Thread(ts)
t1.Start()
t2.Start()
t3.Start()
t4.Start()
End Sub
В этом примере мы начинаем 4 темы каждый из которых выполняет код в начале строки. Существует петля, считывающая коммуникационный порт. Если вы проверите это, вы увидите, что он является потокобезопасным, если все чтение происходит внутри Read()
, которое вам нужно будет развивать, конечно. У вас может быть другой уровень, в котором вы отправляете пользовательские команды и ожидаете ответа. Эти два действия должны быть внутри одного SyncLock в каждой настраиваемой функции. Thread B должен использовать тот же класс, если он делает аналогичную вещь.
для столкновений, почему у вас нет одного класса и доступа к последовательному порту оттуда (таким образом, он только получает открыт один раз,). Кроме того, как насчет добавления буфера и записи ваших данных в следующее свободное место в очереди? – jbutler483
что-то подобное на самом деле предлагается здесь: http: //stackoverflow.com/a/4043762/3436942 – jbutler483
Вы имеете в виду каждую тему, называющую этот класс? Не было бы это потокобезопасным, потому что класс все еще вызывает общий ресурс. Например, один поток может вызвать класс для записи на последовательный порт, в то время как другой может уже считывать его. Или вы хотите, чтобы класс открыл порт и принял сообщения из других потоков, помещая их в очередь действий? Если да, то каким образом я могу поделиться классом между потоками? – user3844416