none
Running a task in the background - what is best method to use nowadays?

    General discussion

  • Hi

    Hoping to get some feedback/advice on what methods people recommend for running a process/task in the background so that form can still be updated with progress/results, to avoid issues like form not responding when task is long running, the ability to cancel operation and basically any other of the benefits of using async

    Up until now I have used background worker and it seems to do the job, but I do notice a lot of articles mention that it is becoming obsolete and with .Net 4.5+ there are other better methods to use

    I have spent a few hours researching this and to be honest still confused as to what is best option and why/how etc

    Seen mention of several different options e.g.

    • System.Threading.Thread
    • System.Threading.Tasks
    • Async / Await
    • Task Parallel Library (TPL)
    • etc

    So thought I would ask on here as I know some of you are experts in VB and would give me pointers/ideas as to best routes so that I could try and learn each method (or at least ones worth learning!)

    One area I am definitely interested in working out is how to run one task after another in the background e.g. for a tool which runs several tests I don't want them all to run together, but want one to start after other finishes until all tasks done

    So assuming I  just had a sub that runs a long running task, for testing just a very simple loop returning count to text box - if I run it normally obviously it freezes up the form, I have got it working using Thread and Background Worker but not sure about others and which ones it the best to use for most things

    For i As Long = 0 To 9000000000000000
          TextBox1.Text = i
          TextBox1.Update()
    Next

    Thread:

    Private Sub btnStartThreadClick(sender As Object, e As EventArgs) Handles btnStartThread.Click
            btnStartThread.Enabled = False
            Thread1 = New Thread(AddressOf ThreadingTest)
            Thread1.IsBackground = True
            Thread1.Start()
    End Sub
    
    Private Sub ThreadingTest()
           For i As Long = 0 To 9000000000000000
                Update_TextBox1(i)
            Next
            Update_TextBox1("Done")
            btnStartThread.Enabled = True
    End Sub
    
    Private Sub Update_TextBox1(text As String)
            If TextBox1.InvokeRequired Then
                TextBox1.Invoke(New Action(Of String)(AddressOf Update_TextBox1), text)
            Else
                TextBox1.Text = text
            End If
    End Sub


    BGW:

    Private Sub btnStartBGW_Click(sender As Object, e As EventArgs) Handles btnStartBGW.Click
    
            AddHandler bgw1.DoWork, AddressOf bgw1DoWork
            AddHandler bgw1.RunWorkerCompleted, AddressOf bgw1RunWorkerCompleted
    
            bgw1.WorkerSupportsCancellation = True
    
            bgw1.RunWorkerAsync()
    
        End Sub
    
        Private Sub bgw1DoWork()
            For i As Long = 0 To 9000000000000000
    
                If bgw1.CancellationPending Then
                    Exit For
                End If
    
                Update_TextBox1(i)
            Next
        End Sub
    
        Private Sub bgw1RunWorkerCompleted()
            Update_TextBox1("Done")
        End Sub
    
        Private Sub btnStopBGW_Click(sender As Object, e As EventArgs) Handles btnStopBGW.Click
            bgw1.CancelAsync()
        End Sub

    All comments gratefully received

    Just trying to learn more about this area from the experts :)


    Darren Rose


    • Edited by wingers Sunday, April 16, 2017 8:06 PM amended for clarity
    Saturday, April 15, 2017 7:21 PM

All replies

  • High level I think which way to go depends on what the work you want to do best suits the path used to keep an application responsive.

    I would suggest reading the following.

    Also consider using iterators and Observer design pattern (which would be used with any of the methods asked about).

    Overall I prefer using Task what have cancellation tokens. If there are several task that run together then there is Task.WhenAll or WhenAny for instance in tangent with ContinueWith.

    Here is a simple example of loading a DataGridView. I made this for a forum question which loaded 100,000,000 records with cancel options keeping the user interface fully responsive.

    Imports System.Data.OleDb 
    Imports System.Threading 
     
    Public Class DataAccess 
     
        Private Builder As New OleDbConnectionStringBuilder With 
            { 
                .DataSource = IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data.accdb"), 
                .Provider = "Microsoft.ACE.OLEDB.12.0" 
            } 
     
        Public Sub New() 
        End Sub 
        Public Function RowCount() As Integer 
     
            Dim Count As Integer = 0 
     
            Using cn As New OleDbConnection With {.ConnectionString = Builder.ConnectionString} 
                Using cmd As New OleDbCommand With {.CommandText = "SELECT COUNT(Identifier) As RowCount FROM People;", .Connection = cn} 
                    cn.Open() 
                    Count = CInt(cmd.ExecuteScalar) 
                End Using 
            End Using 
     
            Return Count 
     
        End Function 
        Public Iterator Function LoadCustomers(ByVal ct As CancellationToken) As IEnumerable(Of Object()) 
     
            Dim RecordCount As Integer = RowCount() 
     
            Using cn As New OleDbConnection With {.ConnectionString = Builder.ConnectionString} 
                Using cmd As New OleDbCommand With {.CommandText = "SELECT Identifier, FirstName, LastName FROM People;", .Connection = cn} 
     
                    cn.Open() 
     
                    Dim Identifier As Integer = 0 
                    Dim FirstName As String = "" 
                    Dim LastName As String = "" 
                    Dim ItemArray As Object() = {} 
     
                    Dim Reader = cmd.ExecuteReader 
     
                    If Reader.HasRows Then 
     
                        While Reader.Read 
                            Identifier = Reader.GetFieldValue(Of Integer)(0) 
                            FirstName = Reader.GetFieldValue(Of String)(1) 
                            LastName = Reader.GetFieldValue(Of String)(2) 
     
                            Yield ItemArray 
     
                            ' update label on calling form 
                            CustomersForm.UpDateDataTable({Identifier, FirstName, LastName}, String.Format("Loading {0} of {1}", Identifier, RecordCount)) 
                            ' update progressbar on calling form 
                            'CustomersForm.UpDateDataTable({Identifier, FirstName, LastName}, Identifier, RecordCount) 
     
     
                        End While 
     
                    End If 
     
                End Using 
            End Using 
        End Function 
    End Class 
    

    Form

    Imports System.Threading 
     
    Public Class CustomersForm 
        Private Const WM_CLOSE As Integer = &H10 
     
        Private tokenSource As New CancellationTokenSource() 
        Private token As CancellationToken = tokenSource.Token 
        Private CurrentlyRunning As Boolean = False 
     
        Private dt As New DataTable With {.TableName = "MyTable"} 
     
        Public Delegate Sub UpdateDataTableDelegate(ByVal value As Object(), ByVal Message As String) 
        Public Delegate Sub UpdateDataTableDelegateProgress(ByVal value As Object(), ByVal CurrentPosition As Integer, ByVal Total As Integer) 
     
        Protected Overrides Sub WndProc(ByRef m As Message) 
            If m.Msg = WM_CLOSE Then 
                'MessageBox.Show("Closing") 
            End If 
            MyBase.WndProc(m) 
        End Sub 
     
        ''' <summary> 
        ''' Version 1: updates a label showing current position and total rows 
        ''' value contains an array of values representing field values from 
        ''' the backend database pushed here from DataAccess.LoadCustomers 
        ''' </summary> 
        ''' <param name="sender"></param> 
        ''' <remarks> 
        ''' I check for a column count which is three unless the operation was  
        ''' inadvertently cancelled i.e. closed form while populating rows. 
        ''' </remarks> 
        Public Sub UpDateDataTable(ByVal sender As Object(), ByVal Message As String) 
            If InvokeRequired Then 
                Invoke(New UpdateDataTableDelegate(AddressOf UpDateDataTable), sender, Message) 
            Else 
                If dt.Columns.Count > 0 Then 
                    dt.Rows.Add(sender) 
                    Label1.Text = Message 
                End If 
            End If 
        End Sub 
        ''' <summary> 
        ''' Version 2: updates a progressbar showing current position 
        ''' value contains an array of values representing field values from 
        ''' the backend database pushed here from DataAccess.LoadCustomers 
        ''' </summary> 
        ''' <param name="sender"></param> 
        ''' <remarks> 
        ''' I check for a column count which is three unless the operation was  
        ''' inadvertently cancelled i.e. closed form while populating rows. 
        ''' </remarks> 
        Public Sub UpDateDataTable(ByVal sender As Object(), ByVal CurrentPosition As Integer, ByVal Total As Integer) 
            If InvokeRequired Then 
                Invoke(New UpdateDataTableDelegateProgress(AddressOf UpDateDataTable), sender, CurrentPosition, Total) 
            Else 
                If dt.Columns.Count > 0 Then 
                    dt.Rows.Add(sender) 
                    ProgressBar1.Value = CurrentPosition 
                    If CurrentPosition = Total Then 
                        MessageBox.Show("Done loading") 
                    End If 
                End If 
            End If 
        End Sub 
        Private Async Sub cmdLoad_Click(sender As Object, e As EventArgs) Handles cmdLoad.Click 
            Label1.Text = "" 
            CurrentlyRunning = True 
            dt.Rows.Clear() 
            CustomersGrid.DataSource = dt 
     
            Dim da As New DataAccess 
     
            ProgressBar1.Maximum = da.RowCount 
     
            For Each item As Object() In da.LoadCustomers(token) 
     
                If token.IsCancellationRequested Then 
                    Exit For 
                End If 
     
                Await Task.Factory.StartNew(Sub() Thread.Sleep(1), token) 
     
            Next 
     
            If token.IsCancellationRequested Then 
                tokenSource = New CancellationTokenSource() 
                token = tokenSource.Token 
     
                If chkClearRows.Checked Then 
                    dt.Rows.Clear() 
                End If 
     
            End If 
        End Sub 
        Private Sub cmdCancel_Click(sender As Object, e As EventArgs) Handles cmdCancel.Click 
     
     
     
            If My.Dialogs.Question("Cancel loading of data?") Then 
                CurrentlyRunning = False 
                tokenSource.Cancel() 
            End If 
        End Sub 
        Private Sub CustomersForm_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing 
            If CurrentlyRunning Then 
                If Not token.IsCancellationRequested Then 
                    tokenSource.Cancel() 
                End If 
            End If 
        End Sub 
        Private Sub CustomersForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load 
            dt.Columns.Add(New DataColumn With {.ColumnName = "Identifier", .DataType = GetType(Int32)}) 
            dt.Columns.Add(New DataColumn With {.ColumnName = "FirstName", .DataType = GetType(String)}) 
            dt.Columns.Add(New DataColumn With {.ColumnName = "LastName", .DataType = GetType(String)}) 
            Label1.Text = "" 
        End Sub 
    End Class 
    Help us im


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    Saturday, April 15, 2017 10:55 PM
    Moderator
  • Hi wingers,

    I attach importance to readability in code, if they bring the same result.
    I used BackgroundWorker a few years ago, and now Async / Await.
    ______________________
    Ashidacchi @self-employed
    Saturday, April 15, 2017 10:57 PM
  • Darren,

    For a reply on this your question is incomplete. 

    ......................................................and to avoid issues like form not responding etc

    I think I've suggested you already in past not to use a windows forms but a service, but you tell than always that there is no need for it. However, there is as you state your question not even a need for a backgroundworker. Form not responding is no problem. The user can simply do something else or wait and that is far most the best or there should be something that makes that he must be able to repond.


    Success
    Cor


    Sunday, April 16, 2017 8:02 AM
  • Darren,

    For a reply on this your question is incomplete. 

    ......................................................and to avoid issues like form not responding etc

    I think I've suggested you already in past not to use a windows forms but a service, but you tell than always that there is no need for it. However, there is as you state your question not even a need for a backgroundworker. Form not responding is no problem. The user can simply do something else or wait and that is far most the best or there should be something that makes that he must be able to repond.


    Success
    Cor

    For some of my apps yes I have been using a windows service and it works perfectly fine

    For the apps I am talking about they are windows form based app and require a fully functional GUI - so using a service for these is not an option

    Hence asking the question about which Microsoft technology/solution is best to use for running long running tasks in the background

    Really can't see the issue with my question/discussion to be honest...?


    Darren Rose

    Sunday, April 16, 2017 11:13 AM

  • Really can't see the issue with my question/discussion to be honest...?


    Darren Rose

    Darren,

    First of all, you created a discussion, that has to be done deliberately in this forum and you are a regular, therefore you should know that it is not a question.


    In a discussion I tell what I think and that is that with what you describe you don't need an async solution at all. That seems to me to try async and not to help end users.

    But maybe I focus me to much on how I can make programs as much as understandable for those who use my programs and not on to show how good I am.

     


    Success
    Cor


    Sunday, April 16, 2017 7:29 PM
  • Yes I chose to start a discussion - as a discussion is what I wanted - to hear from other programmers what methods they use and prefer for running tasks asynchronously

    ***

    For many reasons in several different projects yes I do need to run code asynchronously - and in projects where a form based application is more suited than a windows service - hence the research I have done and starting this discussion to get more information from others

    I have always appreciated yours and others comments and feedback, and yes in the past you were right and a service was the best solution - but that is not always the answer

    I have learnt a lot from these forums and the helpful users like yourself over the years, so I repeat that I really cannot see what is wrong with me starting a discussion on a programming forum asking what async methods other users use, prefer and why

    Since I have always used background workers myself and see that some recommend not using these so much now as other perhaps better solutions

    As I say I appreciate your input but a service is NOT the answer this time as the tools I am creating/improving are very heavily GUI based, so for tasks like scanning the hardware in a machine, searching through event logs for specific entries, searching file system for specific files are all long running tasks that lock up a form - so yes an async method is what I need and I wouldn't have started discussion if it wasn't


    Darren Rose


    • Edited by wingers Sunday, April 16, 2017 7:39 PM
    Sunday, April 16, 2017 7:38 PM
  • Kareninstructor and Ashidacchi - thank you for your replies, I will indeed read the links mentioned

    Darren Rose

    Sunday, April 16, 2017 7:47 PM
  • But you discussed what is the best based on "etc". 

    What reply  do you think you get. For a well reply we need to know what is the "etc". Currently I and others can only think that you want to be able to abort the application quicker than after the finessing of a task. However,  the user can also do other things by simply let the program run and wait until it is ready, there is not anything needed to spent time with let it spent time on managing background processing. I know it was a while seen as a solution for everything, but that is history.  

    I was asking what the "etc"  was but you started to argue that my reply was wrong. 

    By the way, especially in a discussion I tell here the best I know. It is easy to get points, but I'm not interested. You are not the only one viewing this forum you know and I don't want to be seen as a points collector, because then nobody has anything on my replies, then they simply can ignore those if they really wants help.

    Success

    Cor


    Sunday, April 16, 2017 7:48 PM
  • But you discussed what is the best based on "etc". 

    What reply  do you think you get. For a well reply we need to know what is the "etc". Currently I and others can only think that you want to be able to abort the application quicker than after the finessing of a task. However,  the user can also do other things by simply let the program run and wait until it is ready, there is not anything needed to spent time with let it spent time on managing background processing. I know it was a while seen as a solution for everything, but that is history.  

    I was asking what the "etc"  was but you started to argue that my reply was wrong. 


    Success
    Cor

    Sorry I misunderstood you original reply then

    The etc is many things that take a long time to run and where I need to a) update the form with progress/results as they are running, b) have ability to cancel operation c) form to still be functional and not go to not responding and d) basically all the benefits of using async

    This was why my "discussion" was quite open - as it wasn't a "question" on one particular area but a discussion on alternatives to using a background worker to run code asynchronously

    Hope that clears it up - and sorry for misunderstanding


    Darren Rose

    Sunday, April 16, 2017 7:52 PM

  • Sorry I misunderstood you original reply then

    The etc is many things that take a long time to run and where I need to a) update the form with progress/results as they are running, b) have ability to cancel operation c) form to still be functional and not go to not responding and d) basically all the benefits of using async

    This was why my "discussion" was quite open - as it wasn't a "question" on one particular area but a discussion on alternatives to using a background worker to run code asynchronously

    Hope that clears it up - and sorry for misunderstanding


    Darren Rose

    Then keep it as it is and use next time the in the language integrated keywords as are created by my friend Lucian Wischik, be aware, that is not about processing but about better readability of your VB or C# program. 

    https://msdn.microsoft.com/en-us/library/mt674902.aspx

    Be aware that Lucian like me has often written that Async alone is not a solution. It has really to solve something likewise that input is not synchronously given from a device (even if that is Internet). 


    Success
    Cor


    Sunday, April 16, 2017 8:06 PM