(This was originally posted at StackOverflow on 10/14 but I've copied it here per suggestion by @mikalai)
I'm seeking some second opinions regarding the validity of my test.
In a nutshell, I'm using MMF to simulate a synchronous method call from a WinForms client to a Windows Service. Neither WCF nor Named Pipes is a good fit for this, due to the reasons discussed here.
For my prototype I've created two simple applications--a console app represents the WinForms 'client' and a WinForms app represents the WinService 'server.'
I've had to overcome two main problems: 1) The server's timer fires only once every three seconds, and 2) The OS seems to require at least a full second to acquire and then release a mutex. This second point has got me curious. I'd have thought the process was instant, but apparently I'm mistaken.
To accomodate these two issues I've used Thread.Sleep--two seconds to wait for the server's timer and one second to wait for the mutex handling. Intervals of any less than these cause intermittent read/write synchronization failures. Of course feel free to tweak the code to get your own results.
My test is comprised of a 30,000-iteration loop in the client that 'calls' the 'method' in the server and writes the response to a log file. Currently I'm at iteration ~22,500 without any error.
My question: does anyone see any problems with my test? Given the requirements indicated in my MSDN post (linked above), do my results so far indicate a stable design?
That said, if my architecture can be improved I'd be very interested to hear about it.
Here's the code:
Console (client)
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Threading
Imports System.Text
Imports System.Security.Cryptography
Module Main
Sub Main()
Dim _
sForward,
sReverse,
sOutput As String
Console.WriteLine("Enter a string to reverse:")
For iCount As Integer = 0 To 29999
sForward = GetRandomString(7)
sReverse = ReverseString(sForward)
sOutput = sForward & " => " & sReverse
File.AppendAllText("Output.log", sOutput & vbCrLf)
Console.WriteLine(sOutput)
Thread.Sleep(1000)
Next
Console.ReadLine()
End Sub
Private Function GetRandomString(Length As Integer) As String
Dim sSeedText As String
Dim oBuilder As StringBuilder
Dim aBuffer As Byte()
Dim oCrypto As RNGCryptoServiceProvider
sSeedText = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
oBuilder = New StringBuilder
aBuffer = New Byte(Length - 1) {}
oCrypto = New RNGCryptoServiceProvider
oCrypto.GetNonZeroBytes(aBuffer)
For Each bByte As Byte In aBuffer
oBuilder.Append(sSeedText.Chars(bByte Mod sSeedText.Length))
Next
Return oBuilder.ToString
End Function
Private Function ReverseString(Data As String) As String
Dim lIsOwner As Boolean
Dim iLength As Integer
Dim aData As Byte()
aData = Encoding.Unicode.GetBytes(Data)
iLength = aData.Length
Using oFile As MemoryMappedFile = MemoryMappedFile.CreateNew("{99EC7026-0059-4D48-99B1-B400BDACBDD8}", 1024)
Using oMutex As New Mutex(True, "{35C4F5C6-874B-4536-901A-D5382B7E2B9C}", lIsOwner)
''*=====================================================================
' Wait for the server's Timer.Tick event to fire
''*=====================================================================
Thread.Sleep(2000)
''*=====================================================================
Using oStream As MemoryMappedViewStream = oFile.CreateViewStream(0, 0)
Using oWriter As New BinaryWriter(oStream)
oWriter.Write(iLength)
oWriter.Write(aData)
End Using
End Using
oMutex.ReleaseMutex()
'*====================================================================='
' Wait for the server to acquire and then release the mutex '
'*====================================================================='
Thread.Sleep(1000)
'*====================================================================='
oMutex.WaitOne()
Using oStream As MemoryMappedViewStream = oFile.CreateViewStream(iLength + 4, 0)
Using oReader As New BinaryReader(oStream)
With oReader
aData = .ReadBytes(.ReadInt32)
End With
End Using
End Using
oMutex.ReleaseMutex()
End Using
End Using
Return Encoding.Unicode.GetString(aData)
End Function
End Module
WinForms (server)
'*============================================================================='
' '
' Add a ListBox '
' Add a Background Worker '
' Add a Windows Forms Timer '
' Interval = 3000 '
' Enabled = True '
' '
'*============================================================================='
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.ComponentModel
Imports System.Threading
Imports System.Text
Public Class Main
Private Sub tmrTimer_Tick(Sender As Object, e As EventArgs) Handles tmrTimer.Tick
Dim lIsOpen As Boolean
Dim oFile As MemoryMappedFile
tmrTimer.Stop()
Try
lIsOpen = True
oFile = MemoryMappedFile.OpenExisting("{99EC7026-0059-4D48-99B1-B400BDACBDD8}")
bgwWorker.RunWorkerAsync(oFile)
Catch ex As FileNotFoundException
lIsOpen = False
End Try
If Not lIsOpen Then
tmrTimer.Start()
End If
End Sub
Private Sub bgwWorker_DoWork(Sender As Object, e As DoWorkEventArgs) Handles bgwWorker.DoWork
Dim iLength As Integer
Dim sData As String
Dim aData As Byte()
Using oFile As MemoryMappedFile = e.Argument
Using oMutex As Mutex = Mutex.OpenExisting("{35C4F5C6-874B-4536-901A-D5382B7E2B9C}")
oMutex.WaitOne()
Using oStream As MemoryMappedViewStream = oFile.CreateViewStream(0, 0)
Using oReader As New BinaryReader(oStream)
With oReader
aData = .ReadBytes(.ReadInt32)
End With
End Using
End Using
sData = Encoding.Unicode.GetString(aData)
aData = Encoding.Unicode.GetBytes(New String(sData.Reverse.ToArray))
iLength = aData.Length
Using oStream As MemoryMappedViewStream = oFile.CreateViewStream(iLength + 4, 0)
Using oWriter As New BinaryWriter(oStream)
oWriter.Write(iLength)
oWriter.Write(aData)
End Using
End Using
oMutex.ReleaseMutex()
End Using
End Using
e.Result = sData
End Sub
Private Sub bgwWorker_RunWorkerCompleted(Sender As Object, e As RunWorkerCompletedEventArgs) Handles bgwWorker.RunWorkerCompleted
lstArguments.Items.Add(e.Result)
tmrTimer.Start()
End Sub
End Class