Search code examples
vb.netmultithreadinguser-interfacetaskcontinuewith

VB.net ContinueWith


I have this code which loops through all my accounts in my list and then does something to the accounts using tasks for each account as a way to speed up the process. Each time the program completes this action, I want the user interface to update the progress bar. I was using Invoke before but it isn't the best option and I couldn't get it working. Now I know this can be done using a background worker but this isn't the best way of making your application multithreaded so I used this. And instead of invoking I heard about ContinueWith but I can't seem to get it working and I get no error message just a red underline. Code:

progressBar.Value = 0
    Dim tasks As New List(Of Task)()
    For Each account In combos
        Dim t As Task = Task.Run(Sub()
                                     While checked = False
                                         If proxies.Count = 0 Then
                                             Exit Sub
                                             'Also can't think of a good way to stop searching through accounts when there are no proxies left in my queue.
                                         End If
                                         Dim proxy As New WebProxy(proxies(0))
                                         proxies.TryDequeue(0)
                                         'Do something
                                     End While
                                     checkedAmount += 1
                                     Dim progress As Integer = ((checkedAmount / combos.Count) * 100)
                                     Task.ContinueWith(progressBar.Value = progress, TaskScheduler.FromCurrentSynchronizationContext()) 'Error here
                                 End Sub)
        tasks.Add(t)
    Next
    Task.WaitAll(tasks.ToArray())

I get no error code as shown here: enter image description here

I have also tried putting a sub after and stuff but that lead to nothing. Thanks for any help in advance.

Update tried with invoke:

 Private Delegate Sub UpdateProgressBarDelegate(ByVal progressBarUpdate As ProgressBar, ByVal value As Integer)

Dim checkedAmount As Integer = 0
Dim checked As Boolean = False
Private Sub startBtn_Click(sender As Object, e As EventArgs) Handles startBtn.Click
    progressBar.Value = 0
    Dim tasks As New List(Of Task)()
    For Each account In combos
        Dim t As Task = Task.Run(Sub()
                                     While checked = False
                                         proxies.TryDequeue(0)
                                         'do stuff
                                     End While
                                     checkedAmount += 1
                                     Dim progress As Integer = ((checkedAmount / combos.Count) * 100)
                                     If Me.InvokeRequired = True Then
                                         Me.Invoke(New UpdateProgressBarDelegate(AddressOf UpdateProgressBar), progressBar, progress)
                                     Else
                                         UpdateProgressBar(progressBar, progress)
                                     End If
                                     'Task.ContinueWith(progressBar.Value = progress, TaskScheduler.FromCurrentSynchronizationContext())
                                 End Sub)
        tasks.Add(t)
    Next
    Task.WaitAll(tasks.ToArray())
End Sub

Private Sub UpdateProgressBar(ByVal ProgressBarUpdate As ProgressBar, progress As Integer)
    progressBar.Value = progress
End Sub

Still doesn't work not sure why?


Solution

  • Now I know this can be done using a background worker but this isn't the best way of making your application multithreaded

    Sort of.

    BackgroundWorker is a poor way to run many different Tasks individually. No one wants to deal with a separate BackgroundWorker component for each Task. But one BackgroundWorker is a great way to spawn just one extra thread to manage all your other Tasks and update the progress bar. It's an easy solution here.

    Either way, the one thing you'll want to do for sure is move the code to update the ProgressBar out of the individual Tasks. Having that inside a Tasks violates separation of concerns1. Once you do that, you'll also need to change the call to WaitAll() to use WaitAny() in a loop that knows how many tasks you have, so you can still update the ProgressBar as each Task finishes. This will likley have the side effect of fixing your current issue, as well.

    Private Async Sub startBtn_Click(sender As Object, e As EventArgs) Handles startBtn.Click
    
        Dim tasks As New List(Of Task)()
        For Each account In combos
           Dim t As Task = Task.Run(Sub()
                                     While Not checked
                                         proxies.TryDequeue(0)
                                         'do stuff
                                     End While
                                   End Sub)
            tasks.Add(t)
        Next
    
    
        progressBar.Value = 0
        For i As Integer = 1 To tasks.Count 
            Dim t = Await Task.WhenAny(tasks) 
            tasks.Remove(t)
            progressBar.Value  = (i / combos.Count) * 100
        Next i
    End Sub
    

    1 The problem here illustrates one reason we care about separation of concerns at all. Once I fix this, the code becomes much simpler and the frustrating errors just go away.