For this example, we will design an application to remotely monitor the temperature and memory usage of the PC. Each monitoring function runs in a separate thread and monitors corresponding elements to produce a value every 1 second. This information needs to be relayed across to the remote device connected via the Serial communication interface of the PC.
Well with requirements as simple as this, let’s dig in
TemperatureMonitor Class ListingPublic Class TemperatureMonitor
Private _IsExitSignalled As Boolean
Public Sub New()
_IsExitSignalled = False
End Sub
Public Sub StartMonitoring()
Dim oSerialCommunicator As SerialCommunicator = New SerialCommunicator
Dim th As System.Threading.Thread = New System.Threading.Thread(AddressOf _MonitoringThread)
th.Start(oSerialCommunicator)
End Sub
Private Sub _MonitoringThread(ByVal oSerialCommunicator As Object)
Dim oSC As SerialCommunicator = CType(oSerialCommunicator, SerialCommunicator)
Do
'Add steps here to perform actual temperatur monitoring,
oSC.SendMonitoredInfo("Memory Temperature Message")
Threading.Thread.Sleep(2000) 'Sleep for 2 seconds
Loop Until _IsExitSignalled
End Sub
Public Sub SignalExit()
_IsExitSignalled = True
End Sub
End Class
MemoryMonitor Class ListingPublic Class MemoryMonitor
Private _IsExitSignalled As Boolean
Public Sub New()
_IsExitSignalled = False
End Sub
Public Sub StartMonitoring()
Dim oSerialCommunicator As SerialCommunicator = New SerialCommunicator
Dim th As System.Threading.Thread = New System.Threading.Thread(AddressOf _MonitoringThread)
th.Start(oSerialCommunicator)
End Sub
Private Sub _MonitoringThread(ByVal oSerialCommunicator As Object)
Dim oSC As SerialCommunicator = CType(oSerialCommunicator, SerialCommunicator)
Do
'Add steps here to perform actual memory monitoring,
oSC.SendMonitoredInfo("Memory Monitor Message")
Threading.Thread.Sleep(2000) 'Sleep for 2 seconds
Loop Until _IsExitSignalled
End Sub
Public Sub SignalExit()
_IsExitSignalled = True
End Sub
End Class
SerialCommunicator Class ListingPublic Class SerialCommunicator
Public Sub New()
'Open the communication port
Console.WriteLine("Opening Serial Port")
End Sub
Public Sub SendMonitoredInfo(ByVal Message As String)
Console.WriteLine("Sending message " & Message)
End Sub
End Class
And here is what our classes look like
So what else, well the test harness to test our little application
modMain Module ListingModule modMain
Sub Main()
Dim oTemperatureMonitor As TemperatureMonitor = New TemperatureMonitor
Dim oMemoryMonitor As MemoryMonitor = New MemoryMonitor
oTemperatureMonitor.StartMonitoring()
oMemoryMonitor.StartMonitoring()
Console.WriteLine("Press Enter to stop monitoring")
Console.ReadLine()
oTemperatureMonitor.SignalExit()
oMemoryMonitor.SignalExit()
Console.WriteLine("Monitoring stopped")
Console.ReadLine()
End Sub
End Module
And a first look at the results
All this looks pretty simple on the face of it; however, as any experienced programmer who has used the COM port of a PC will tell you, you cannot concurrently open the same port more than once. This implies that the information that the Temperature Monitoring thread will produce will need to be sent over the serial interface in a timeshared fashion along with the information produced by the Memory Monitoring thread. This also implies that the highlighted portion in the output which indicates that the port has been opened concurrently is an issue and this application cannot be used for sending messages out.
So, how do we fix this port sharing issue? Put the Singleton pattern to use. We transform our SerialCommunicator class into a singleton so that it returns a single instance of itself to all requesting methods. With only a single instance of the SerialCommunicator, we eliminate the issue of opening the serial port concurrently. To transform this class into a Singleton, the first thing we need to do is to prevent its external instantiation using the New keyword. We can achieve this by changing the scope of the constructor to Private.
Hold on, won’t that prevent object instantiation? In the conventional sense, Yes! However a private constructor implies that the object can be instantiated within the class. What good is that? How will our TemperatureMonitor and MemoryMonitor acquire this instance? Ok, Enough beating around the bush. Just add a shared method that creates and returns an instance of the class. Ensure that it creates an instance only if an instance of the class does not exist. If an instance exists, it simply returns that instance.
Well, here is the new and improved Singleton SerialCommunicator class –
SerialCommunicator Class ListingPublic Class SerialCommunicator
Private Shared _oSerialCommunicator As SerialCommunicator
Private Sub New()
'Open the communication port
Console.WriteLine("Opening Serial Port")
End Sub
Public Shared Function GetSerialCommunicator() As SerialCommunicator
If _oSerialCommunicator Is Nothing Then
_oSerialCommunicator = New SerialCommunicator
End If
Return _oSerialCommunicator
End Function
Public Sub SendMonitoredInfo(ByVal Message As String)
SyncLock Me
Console.WriteLine("Sending message " & Message)
End SyncLock
End Sub
End Class
We need to change our TemperatureMonitor and MemoryMonitor classes as well since the public constructor of the SerialCommunicator is no longer available.
TemperatureMonitor StartMonitoring Method ListingPublic Class TemperatureMonitor
'No change in code here
Public Sub StartMonitoring()
Dim oSerialCommunicator As SerialCommunicator = SerialCommunicator.GetSerialCommunicator
Dim th As System.Threading.Thread = New System.Threading.Thread(AddressOf _MonitoringThread)
th.Start(oSerialCommunicator)
End Sub
'No change in code or here
End Class
MemoryMonitor StartMonitoring Method ListingPublic Class MemoryMonitor
'No change in code here
Public Sub StartMonitoring()
Dim oSerialCommunicator As SerialCommunicator = SerialCommunicator.GetSerialCommunicator
Dim th As System.Threading.Thread = New System.Threading.Thread(AddressOf _MonitoringThread)
th.Start(oSerialCommunicator)
End Sub
'No change in code here
End Class
Our class diagram is identical to our original. The only difference being the scope of the SerialCommunicator constructor and an additional method in the same class to create and return an instance of the SerialCommunicator class.
With no change in our test harness, let’s put our application to the test
Notice that we only have one “Opening Serial Port” message indicating our Singleton pattern is in effect.