Worker Pools in Visual Basic .NET

In this example we’ll look at how to implement a worker pool using threads and queues.

Imports System
Imports System.Threading
Imports System.Collections.Concurrent

Module WorkerPools
    ' Here's the worker, of which we'll run several
    ' concurrent instances. These workers will receive
    ' work on the `jobs` queue and send the corresponding
    ' results on `results`. We'll sleep a second per job to
    ' simulate an expensive task.
    Sub Worker(id As Integer, jobs As BlockingCollection(Of Integer), results As BlockingCollection(Of Integer))
        For Each j In jobs.GetConsumingEnumerable()
            Console.WriteLine($"worker {id} started  job {j}")
            Thread.Sleep(1000)
            Console.WriteLine($"worker {id} finished job {j}")
            results.Add(j * 2)
        Next
    End Sub

    Sub Main()
        ' In order to use our pool of workers we need to send
        ' them work and collect their results. We make 2
        ' queues for this.
        Const numJobs As Integer = 5
        Dim jobs As New BlockingCollection(Of Integer)(numJobs)
        Dim results As New BlockingCollection(Of Integer)(numJobs)

        ' This starts up 3 workers, initially blocked
        ' because there are no jobs yet.
        For w As Integer = 1 To 3
            Dim workerID As Integer = w
            Dim thread As New Thread(Sub() Worker(workerID, jobs, results))
            thread.Start()
        Next

        ' Here we send 5 `jobs` and then `close` that
        ' queue to indicate that's all the work we have.
        For j As Integer = 1 To numJobs
            jobs.Add(j)
        Next
        jobs.CompleteAdding()

        ' Finally we collect all the results of the work.
        ' This also ensures that the worker threads have
        ' finished. An alternative way to wait for multiple
        ' threads is to use Thread.Join().
        For a As Integer = 1 To numJobs
            results.Take()
        Next
    End Sub
End Module

Our running program shows the 5 jobs being executed by various workers. The program only takes about 2 seconds despite doing about 5 seconds of total work because there are 3 workers operating concurrently.

$ vbc WorkerPools.vb
$ mono WorkerPools.exe
worker 1 started  job 1
worker 3 started  job 3
worker 2 started  job 2
worker 1 finished job 1
worker 1 started  job 4
worker 3 finished job 3
worker 2 finished job 2
worker 2 started  job 5
worker 1 finished job 4
worker 2 finished job 5

real    0m2.358s

In this Visual Basic .NET version:

  1. We use Thread instead of goroutines for concurrency.
  2. BlockingCollection(Of T) is used in place of channels for thread-safe communication between threads.
  3. The Worker function is defined as a Sub that takes a BlockingCollection(Of Integer) for both jobs and results.
  4. We use Thread.Sleep(1000) to simulate work instead of time.Sleep(time.Second).
  5. The main loop creates new Thread instances and starts them.
  6. We use jobs.CompleteAdding() to signal that no more items will be added to the jobs collection.
  7. The results are collected using results.Take() which blocks until an item is available.

This implementation maintains the core concept of the worker pool pattern, demonstrating how to distribute work across multiple threads and collect results in Visual Basic .NET.