0

I need to spawn several background threads for the ThreadPool (my test store contains 6008 part numbers, and the individual threads would look up each part's barcode and description from the corporate database).

I am getting ideas from this post: Wait for pooled threads to complete

I am interested in trying JaredPar's answer without the WaitHandle (since it has a 64 thread limit), but I don't know how to duplicate his QueueUserWorkItem in this VB application:

public static void SpawnAndWait(IEnumerable<Action> actions)
{
    var list = actions.ToList();
    var handles = new ManualResetEvent[actions.Count()];
    for (var i = 0; i < list.Count; i++)
    {
        handles[i] = new ManualResetEvent(false);
        var currentAction = list[i];
        var currentHandle = handles[i];
        Action wrappedAction = () => { try { currentAction(); } finally { currentHandle.Set(); } };
        ThreadPool.QueueUserWorkItem(x => wrappedAction());
    }
    WaitHandle.WaitAll(handles);
}

How would x => wrappedAction() be written in VB?

It seems like a simple task, but I cannot find any information on how to convert it.

Below is a little VB stub I wrote, trying to get this working.

Using what someone found in Invoke anonymous methods, I tried writing it different ways.

Private m_db As DatabaseHelper

Public Sub Test1(store As StoreLocation)
    For index As Integer = 0 To store.Parts.Count - 1
        Dim item As StorePart = store.Parts(index)
        Dim job As New Action(
            Sub()
              Dim lines As String() = m_db.GetDetails(store.ID, item.PartNumber)
              item.UPC = lines(0)
              item.Vendor = lines(1)
              item.Description = lines(2)
            End Sub
        )
        ThreadPool.QueueUserWorkItem(job()) ' See 1.
        ThreadPool.QueueUserWorkItem(job.Invoke()) ' See 2.
        ThreadPool.QueueUserWorkItem(job) ' See 3.
    Next
End Sub

No, it does not compile.

  1. Expression does not produce a value
  2. Expression does not produce a value
  3. Value of type 'System.Action' cannot be converted to 'System.Threading.WaitCallback'

This project compiles to .NET 4.0, so I can't really use 4.5 await features unless plugins are authorized for the corporate project.

Community
  • 1
  • 1
  • 2
    Have you considered letting the [Task Parallel Library](https://msdn.microsoft.com/en-us/library/dd460717%28v=vs.110%29.aspx) handle the threading details? – asawyer Feb 19 '15 at 15:39
  • I'm not great at doing this stuff in my preferred C# language. Can you show me how to do it in VB? –  Feb 19 '15 at 15:40
  • As for syntax I'm pretty sure the translation from c# would be more like `ThreadPool.QueueUserWorkItem(Function(x) job())` – asawyer Feb 19 '15 at 15:42
  • Still shows squiggle under `job()` with **Expression does not produce a value** –  Feb 19 '15 at 15:43

3 Answers3

4

How would x => wrappedAction() be written in VB?

Well it is 

Sub(x) wrappedAction()

Although the x is irrelevant as it is not used, so you could use Sub() wrappedAction() instead

So something like this should do:

Public Shared Sub SpawnAndWait(ByVal actions As IEnumerable(Of Action))
    Dim list = actions.ToList()
    Dim [handles] = New ManualResetEvent(actions.Count() - 1){}
    For i = 0 To list.Count - 1
        [handles](i) = New ManualResetEvent(False)
        Dim currentAction = list(i)
        Dim currentHandle = [handles](i)
        Dim wrappedAction As Action = Sub()
            Try
                currentAction()
            Finally
                currentHandle.Set()
            End Try
        End Sub
        ThreadPool.QueueUserWorkItem(Sub(x) wrappedAction())
    Next i
    WaitHandle.WaitAll([handles])
End Sub
Matt Wilko
  • 26,994
  • 10
  • 93
  • 143
2

The WaitCallback delegate is defined as

<ComVisibleAttribute(True)> _
Public Delegate Sub WaitCallback(state As Object)

So the correct syntax would be:

ThreadPool.QueueUserWorkItem(Sub(state As Object) wrappedAction())

ThreadPool.QueueUserWorkItem(Sub(state As Object) job())
ThreadPool.QueueUserWorkItem(Sub(state As Object) job.Invoke())
ThreadPool.QueueUserWorkItem(Sub(state As Object) job())
Bjørn-Roger Kringsjå
  • 9,849
  • 6
  • 36
  • 64
0

Taking the C# code, compiling it, then running the assembly through JustDecompile and selecting VB as the output language, I got:

Public Shared Sub SpawnAndWait(ByVal actions As IEnumerable(Of System.Action))
    Dim list As List(Of System.Action) = actions.ToList(Of System.Action)()
    Dim handles(actions.Count(Of System.Action)()) As System.Threading.ManualResetEvent
    Dim i As Integer = 0
    While i < list.Count
        handles(i) = New System.Threading.ManualResetEvent(False)
        Dim item As System.Action = list(i)
        Dim manualResetEvent As System.Threading.ManualResetEvent = handles(i)
        Dim action As System.Action = Sub()
        Try
            item()
        Finally
            manualResetEvent.[Set]()
        End Try
        End Sub
        ThreadPool.QueueUserWorkItem(Sub(x As Object) action())
        i = i + 1
    End While
    WaitHandle.WaitAll(handles)
End Sub
Craig W.
  • 17,838
  • 6
  • 49
  • 82
  • That is a unique way of approaching it - and it works, too! :) –  Feb 19 '15 at 16:07
  • Actually, there a couple of mistakes in that code that you might want to mention to JustDecompile: 1. You can't use 'handles' as an identifier unless you enclose it in square brackets and 2. the array specifier on 'handles' needs to be reduced by 1 since VB arrays are declared using upper bound, not size (this is a critical error on the part of a decompiler). – Dave Doknjas Feb 19 '15 at 17:21