Mutexes in Visual Basic .NET
In the previous example we saw how to manage simple counter state using atomic operations. For more complex state we can use a mutex to safely access data across multiple threads.
Imports System
Imports System.Collections.Generic
Imports System.Threading
Public Class Container
Private ReadOnly _lock As New Object()
Private ReadOnly _counters As New Dictionary(Of String, Integer)
Public Sub New()
_counters("a") = 0
_counters("b") = 0
End Sub
Public Sub Increment(name As String)
SyncLock _lock
_counters(name) += 1
End SyncLock
End Sub
Public Function GetCounters() As Dictionary(Of String, Integer)
Return New Dictionary(Of String, Integer)(_counters)
End Function
End Class
Public Module Program
Public Sub Main()
Dim c As New Container()
Dim doIncrement = Sub(name As String, n As Integer)
For i As Integer = 0 To n - 1
c.Increment(name)
Next
End Sub
Dim tasks As New List(Of Task)()
tasks.Add(Task.Run(Sub() doIncrement("a", 10000)))
tasks.Add(Task.Run(Sub() doIncrement("a", 10000)))
tasks.Add(Task.Run(Sub() doIncrement("b", 10000)))
Task.WaitAll(tasks.ToArray())
Console.WriteLine(String.Join(", ", c.GetCounters().Select(Function(kvp) $"{kvp.Key}: {kvp.Value}")))
End Sub
End ModuleThe Container class holds a dictionary of counters. Since we want to update it concurrently from multiple threads, we add a lock object to synchronize access. Note that in VB.NET, we use SyncLock instead of a Mutex for simplicity in this example.
The Increment method locks the object before accessing _counters, ensuring thread-safe access.
In the Main method, we create an instance of Container and define a lambda function doIncrement that increments a named counter in a loop.
We then create multiple tasks to run doIncrement concurrently, simulating concurrent access to the same Container instance.
After waiting for all tasks to complete, we print the final state of the counters.
Running the program shows that the counters are updated as expected:
a: 20000, b: 10000In this VB.NET version, we’ve used Tasks and the Task.Run method to create concurrent operations, which is analogous to goroutines in Go. The SyncLock statement provides mutual exclusion, similar to the Mutex in the original Go code.
Next, we could look at implementing this same state management task using only asynchronous methods and message passing, which would be more similar to the concept of goroutines and channels in Go.