Stateful Goroutines in Visual Basic .NET
Our example demonstrates how to manage state using threads and message passing in Visual Basic .NET. This approach aligns with the idea of sharing memory by communicating and having each piece of data owned by exactly one thread.
Imports System
Imports System.Collections.Generic
Imports System.Threading
Imports System.Threading.Tasks
Module StatefulThreads
' These classes encapsulate read and write operations
Class ReadOp
Public Key As Integer
Public Result As TaskCompletionSource(Of Integer)
End Class
Class WriteOp
Public Key As Integer
Public Val As Integer
Public Result As TaskCompletionSource(Of Boolean)
End Class
Sub Main()
Dim readOps As Long = 0
Dim writeOps As Long = 0
' Channels for read and write requests
Dim reads As New BlockingCollection(Of ReadOp)()
Dim writes As New BlockingCollection(Of WriteOp)()
' This thread owns the state
Dim stateThread = New Thread(Sub()
Dim state As New Dictionary(Of Integer, Integer)()
While True
Dim readOp As ReadOp = Nothing
Dim writeOp As WriteOp = Nothing
If reads.TryTake(readOp, 0) Then
If state.ContainsKey(readOp.Key) Then
readOp.Result.SetResult(state(readOp.Key))
Else
readOp.Result.SetResult(0)
End If
ElseIf writes.TryTake(writeOp, 0) Then
state(writeOp.Key) = writeOp.Val
writeOp.Result.SetResult(True)
Else
Thread.Sleep(1)
End If
End While
End Sub)
stateThread.Start()
' Start 100 read threads
For r = 0 To 99
Dim t = New Thread(Sub()
Dim rnd As New Random()
While True
Dim read As New ReadOp With {
.Key = rnd.Next(5),
.Result = New TaskCompletionSource(Of Integer)()
}
reads.Add(read)
read.Result.Task.Wait()
Interlocked.Increment(readOps)
Thread.Sleep(1)
End While
End Sub)
t.Start()
Next
' Start 10 write threads
For w = 0 To 9
Dim t = New Thread(Sub()
Dim rnd As New Random()
While True
Dim write As New WriteOp With {
.Key = rnd.Next(5),
.Val = rnd.Next(100),
.Result = New TaskCompletionSource(Of Boolean)()
}
writes.Add(write)
write.Result.Task.Wait()
Interlocked.Increment(writeOps)
Thread.Sleep(1)
End While
End Sub)
t.Start()
Next
' Let the threads work for a second
Thread.Sleep(1000)
' Report the operation counts
Console.WriteLine($"readOps: {readOps}")
Console.WriteLine($"writeOps: {writeOps}")
End Sub
End Module
In this example, we use a dedicated thread to manage the state, which is a dictionary. Other threads send read and write requests to this state-owning thread via BlockingCollection(Of T)
, which serves as our channel equivalent.
We start 100 read threads and 10 write threads. Each thread continuously sends requests to the state-owning thread and waits for the response.
The state-owning thread processes read and write requests as they come in, ensuring that the state is accessed in a thread-safe manner.
We use Interlocked.Increment
to safely count the number of operations performed.
After letting the threads run for a second, we print out the total number of read and write operations performed.
Running this program might show output similar to this:
readOps: 71708
writeOps: 7177
This thread-based approach in Visual Basic .NET is more involved than using locks (the equivalent of mutexes in Go). However, it can be useful in certain scenarios, especially when dealing with complex synchronization requirements or when you want to explicitly control the flow of data between threads.
Choose the approach that feels most natural and makes it easiest to reason about the correctness of your program. In Visual Basic .NET, you have various synchronization primitives at your disposal, including locks, events, and concurrent collections, which you can use based on your specific needs.