title: Why do my tasks don't run parallel?
body: <p>I put together a little test program to learn about multi threading in VB.net. After a lot of researching, trying and failing I got my program running, at least a bit.</p>
  
  <p>Now it behaves oddly and I can't explain why. Maybe you can tell me, what's wrong with my program, why it behaves the way it does and/or what I have to change to get it working the way it's supposed to work.</p>
  
  <p>My program contains a class (TestClass), that simulates the long running tasks that I want to each execute on a different thread, a progress form (Form1) to show progress messages from my long running tasks and the main procedure, which puts these two together.</p>
  
  <p>Here's my code:</p>
  
  <p><strong>Progress form</strong> (Form1) containing two listboxes (ListBox1 and ListBox2):</p>
  
  <pre><code>Public Class Form1
  
  Public Sub AddMessage1(ByVal message As String)
    If String.IsNullOrEmpty(message) Then
      Exit Sub
    End If
  
    Me.ListBox1.Items.Add(message)
    Me.ListBox1.SelectedIndex = Me.ListBox1.Items.Count - 1
    Application.DoEvents()
  End Sub
  
  Public Sub AddMessage2(ByVal message As String)
    If String.IsNullOrEmpty(message) Then
      Exit Sub
    End If
  
    Me.ListBox2.Items.Add(message)
    Me.ListBox2.SelectedIndex = Me.ListBox2.Items.Count - 1
    Application.DoEvents()
  End Sub
  
  End Class
  </code></pre>
  
  <p><strong>TestClass</strong>:</p>
  
  <pre><code>Imports System.Threading
  
  Public Class TestClass
  
    Public Event ShowProgress(ByVal message As String)
  
    Private _milliSeconds As UShort
    Private _guid As String
  
    Public Sub New(milliSeconds As UShort, ByVal guid As String)
      _milliSeconds = milliSeconds
      _guid = guid
    End Sub
  
    Public Function Run() As UShort
      For i As Integer = 1 To 20
        RaiseEvent ShowProgress("Run " &amp; i)
        Thread.Sleep(_milliSeconds)
      Next i
  
      Return _milliSeconds
    End Function
  
  End Class
  </code></pre>
  
  <p><strong>Main procedure</strong>:</p>
  
  <pre><code>Imports System.Threading.Tasks
  Imports System.ComponentModel
  
  Public Class Start
    Private Const MULTI_THREAD As Boolean = True
  
    Public Shared Sub Main()
      Dim testClass(1) As TestClass
      Dim testTask(1) As Task(Of UShort)
      Dim result(1) As UShort
  
      testClass(0) = New TestClass(50, "Test1")
      testClass(1) = New TestClass(200, "Test2")
  
      Using frm As Form1 = New Form1
        frm.Show()
  
        AddHandler testClass(0).ShowProgress, AddressOf frm.AddMessage1
        AddHandler testClass(1).ShowProgress, AddressOf frm.AddMessage2
  
        If MULTI_THREAD Then
          testTask(0) = Task(Of UShort).Factory.StartNew(Function() testClass(0).Run, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext)
          testTask(1) = Task(Of UShort).Factory.StartNew(Function() testClass(1).Run, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext)
  
          Task.WaitAll(testTask)
  
          result(0) = testTask(0).Result
          result(1) = testTask(1).Result
        Else
          result(0) = testClass(0).Run
          result(1) = testClass(1).Run
        End If
  
        RemoveHandler testClass(0).ShowProgress, AddressOf frm.AddMessage1
        RemoveHandler testClass(1).ShowProgress, AddressOf frm.AddMessage2
  
        frm.Close()
      End Using
  
      MessageBox.Show("Result 1: " &amp; result(0) &amp; "; Result 2: " &amp; result(1))
    End Sub
  
  End Class
  </code></pre>
  
  <p>This program is supposed to open the progress form, run the two long running tasks in parallel, show the progress messages of the two long running tasks, close the progress form when both long running tasks are finished and show a message box with the results of the long running tasks.</p>
  
  <p>Now to the behaviour I can't explain: If I run this program, it shows a "Run 1" in both listboxes, so both tasks started running, but then only the first listbox gets filled and only when the first listbox is filled (the first task completed), the second listbox continues getting filled. So, both tasks get started, but the second one waits for the first one to finish before continuing running.<br>
  But, when I comment out the <code>Task.WaitAll(testTask)</code> in my main procedure, it's the other way around. It shows a "Run 1" in both listboxes, then the second listbox gets filled and only when the second listbox is filled (the second task completed), the first one continues running.</p>
  
  <p>Why does my program behaves so oddly and how can I make it run my two tasks at the same time?</p>
  
  <p>Thank you for your help in solving this little mystery     :-)</p>
  
  <hr>
  
  <p><strong>Update</strong><br>
  Since the only answer so far stated that it has to do with the <code>SynchronizationContext</code> I'm using I removed it from the code of my main procedure:</p>
  
  <p><strong>Main procedure</strong>:</p>
  
  <pre><code>Imports System.Threading.Tasks
  Imports System.ComponentModel
  
  Public Class Start
    Private Const MULTI_THREAD As Boolean = True
  
    Public Shared Sub Main()
      Dim testClass(1) As TestClass
      Dim testTask(1) As Task(Of UShort)
      Dim result(1) As UShort
  
      testClass(0) = New TestClass(50, "Test1")
      testClass(1) = New TestClass(200, "Test2")
  
      Using frm As Form1 = New Form1
        frm.Show()
  
        AddHandler testClass(0).ShowProgress, AddressOf frm.AddMessage1
        AddHandler testClass(1).ShowProgress, AddressOf frm.AddMessage2
  
        If MULTI_THREAD Then
          testTask(0) = Task(Of UShort).Factory.StartNew(Function() testClass(0).Run)
          testTask(1) = Task(Of UShort).Factory.StartNew(Function() testClass(1).Run)
  
          Task.WaitAll(testTask)
  
          result(0) = testTask(0).Result
          result(1) = testTask(1).Result
        Else
          result(0) = testClass(0).Run
          result(1) = testClass(1).Run
        End If
  
        RemoveHandler testClass(0).ShowProgress, AddressOf frm.AddMessage1
        RemoveHandler testClass(1).ShowProgress, AddressOf frm.AddMessage2
  
        frm.Close()
      End Using
  
      MessageBox.Show("Result 1: " &amp; result(0) &amp; "; Result 2: " &amp; result(1))
    End Sub
  
  End Class
  </code></pre>
  
  <p>To avoid an <code>InvalidOperationException</code> because of illegal cross-thread calls, I changed the code of my form as follows:</p>
  
  <p><strong>Progress form</strong> (Form1) containing two listboxes (ListBox1 and ListBox2):</p>
  
  <pre><code>Public Delegate Sub ShowProgressDelegate(ByVal message As String)
  
  Public Class Form1
  
  Public Sub AddMessage1(ByVal message As String)
    If String.IsNullOrEmpty(message) Then
      Exit Sub
    End If
  
    Debug.Print("out List 1: " &amp; message)
  
    If Me.InvokeRequired Then
      Me.BeginInvoke(New ShowProgressDelegate(AddressOf Me.AddMessage1), message)
    Else
      Debug.Print("in List 1: " &amp; message)
  
      Me.ListBox1.Items.Add(message)
      Me.ListBox1.SelectedIndex = Me.ListBox1.Items.Count - 1
      Application.DoEvents()
    End If
  End Sub
  
  Public Sub AddMessage2(ByVal message As String)
    If String.IsNullOrEmpty(message) Then
      Exit Sub
    End If
  
    Debug.Print("out List 2: " &amp; message)
  
    If Me.InvokeRequired Then
      Me.BeginInvoke(New ShowProgressDelegate(AddressOf Me.AddMessage2), message)
    Else
      Debug.Print("in List 2: " &amp; message)
  
      Me.ListBox2.Items.Add(message)
      Me.ListBox2.SelectedIndex = Me.ListBox2.Items.Count - 1
      Application.DoEvents()
    End If
  End Sub
  
  End Class
  </code></pre>
  
  <p>I just added a few Debug.Prints for debugging and the <code>InvokeRequired</code> parts.</p>
  
  <p>Now both tasks run parallel (I know because of the <code>Debug.Print</code> calls with the "out List" messages) but my progress messages don't show in the listboxes, the according code never gets executed (the <code>Debug.Print</code> calls with the "in List" messages don't get shown in the debug window).</p>
  
  <p>I googled for the right way to do the <code>InvokeRequired</code> part and the way I did it seems to be right, but it doesn't work.<br>
  Can someone please tell me, what's wrong?</p>
  
  <p>Thank you again for your help.</p>