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 Module

The 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: 10000

In 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.