4 Replies - 563 Views - Last Post: 15 June 2012 - 09:21 AM Rate Topic: -----

#1 Celerian  Icon User is offline

  • D.I.C Regular


Reputation: 144
  • View blog
  • Posts: 384
  • Joined: 30-March 12

Question about BackgroundWorkers

Posted 14 June 2012 - 01:35 PM

I have a BackgroundWorker that is controlled by a timer. The BackgroundWorker is started on Form Load, and it runs through 4 different subs that each retrieve a different piece of information from the database, and then update their respective labels on the main form. The background worker then updates some other labels based on the information the subroutines it was controlling had returned.

The first time the background worker is called, it updates all of the labels correctly, but each subsequent time, the labels will not update. I have stepped through the code, and I find that the piece of code that updates the label seems to be where the issue is, but it is not generating any errors.

The BackgroundWorker:
    Private Sub Gearshaft_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles Gearshaft.DoWork
        GetTotalBoards()
        GetActiveBoards()
        GetShippedBoards()
        GetRepairBonepile()

        'Update the Uncompleted Boards
        lblUncompletedBoards.Text = FormatNumber(Int(lblTotalBoards.Text - lblShippedBoards.Text - lblActiveBoards.Text), 0)

        'Update the Percent Shipped
        lblPercentShipped.Text = FormatPercent(Int(lblShippedBoards.Text) / Int(lblTotalBoards.Text))

        'Update the Percent Active
        lblPercentActive.Text = FormatPercent(Int(lblActiveBoards.Text) / Int(lblTotalBoards.Text))

        'Update the Percent Uncompleted
        lblPercentUncompleted.Text = FormatPercent(Int(lblUncompletedBoards.Text) / Int(lblTotalBoards.Text))
    End Sub


One of the Subroutines:
    Public Sub GetTotalBoards()

        sSQL = "SELECT Count(Distinct LotID) "
        sSQL &= "FROM fsWIPReleaseHistory"
        CreateAConnection()
        OpenAConnection()
        CreateTotalBoards()
        Try
            daTotalBoards = New SqlDataAdapter(sSQL, myConnection.ConnectionObject)
            With daTotalBoards
                .Fill(tblTotalBoards)
            End With
        Catch ex As Exception
            Throw New Exception(ex.Message)
        Finally
            daTotalBoards = Nothing
        End Try
        With tblTotalBoards
            Dim iRecords As Integer = .Rows.Count()
            For i As Integer = 0 To iRecords - 1
                frmMain.lblTotalBoards.Text = FormatNumber(.Rows(0).Item(0), 0)
            Next
        End With
        CloseAConnection()
        DestroyTotalBoards()
    End Sub


Again, it works the first time.

Form Load->
BackgroundWorker_DoWork->
Subroutines, labels update->
Wait 30 seconds (timer)->
BackgroundWorker_DoWork->
Subroutines, labels do not update

I assume that maybe this is because that each subsequent time I run the BackgroundWorker, it is creating a new thread to do all the database connectivity and retreival, but since the labels are located on the first thread, they are not allowed to update them because they belong to the first thread.

Just in case, I'll also include the code for the timer that runs every 30 seconds to see if the BackgroundWorker is busy or not. If the BackgroundWorker is busy, I just have it return its progress completed, but I don't do anything with that. If it is not busy, it performs a RunWorkerAsync. Here it is:

    Public Sub Tock(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles HomeRefresh.Tick
        'Timer fires (30 seconds)
        'Check if Gearshaft is Busy.
        'If Idle, Run Thread
        If Gearshaft.IsBusy = True Then
            Gearshaft.ReportProgress(percentProgress)
        Else
            Gearshaft.RunWorkerAsync()
        End If
    End Sub


I find that if I have a RunWorkerCompleted code in there, the labels will update each subsequent time it runs, but not the first time. RunWorkerCompleted:
Private Sub Gearshaft_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles Gearshaft.RunWorkerCompleted
        'Run every 30 seconds

        'Update the Total Boards
        With tblTotalBoards
            Dim iRecords As Integer = .Rows.Count()
            For i As Integer = 0 To iRecords - 1
                lblTotalBoards.Text = FormatNumber(.Rows(0).Item(0), 0)
            Next
        End With
        DestroyTotalBoards()

        'Update the Active Boards
        With tblActiveBoards
            Dim iRecords As Integer = .Rows.Count()
            For i As Integer = 0 To iRecords - 1
                lblActiveBoards.Text = FormatNumber(.Rows(0).Item(0), 0)
            Next
        End With
        DestroyActiveBoards()

        'Update the Shipped Boards
        With tblShippedBoards
            Dim iRecords As Integer = .Rows.Count()
            For i As Integer = 0 To iRecords - 1
                lblShippedBoards.Text = FormatNumber(.Rows(0).Item(0), 0)
            Next
        End With
        DestroyShippedBoards()

        'Update the Repair Bonepile
        With tblRepairBonepile
            Dim iRecords As Integer = .Rows.Count()
            Dim intBonepile As Integer = 0
            Dim intOther As Integer = 0
            For i As Integer = 0 To iRecords - 1
                intBonepile += .Rows(i).Item(1)
                Select Case i
                    Case 0
                        lblBonepile1Name.Text = .Rows(i).Item(0) & ":"
                        lblBonepile1.Text = FormatNumber(.Rows(i).Item(1), 0)
                    Case 1
                        lblBonepile2Name.Text = .Rows(i).Item(0) & ":"
                        lblBonepile2.Text = FormatNumber(.Rows(i).Item(1), 0)
                    Case 2
                        lblBonepile3Name.Text = .Rows(i).Item(0) & ":"
                        lblBonepile3.Text = FormatNumber(.Rows(i).Item(1), 0)
                    Case 3
                        lblBonepile4Name.Text = .Rows(i).Item(0) & ":"
                        lblBonepile4.Text = FormatNumber(.Rows(i).Item(1), 0)
                    Case 4
                        lblBonepile5Name.Text = .Rows(i).Item(0) & ":"
                        lblBonepile5.Text = FormatNumber(.Rows(i).Item(1), 0)
                    Case Else
                        intOther += .Rows(i).Item(1)
                        lblBonepile6Name.Text = .Rows(i).Item(0) & ":"

                End Select
            Next
            lblBonepile6.Text = FormatNumber(intOther, 0)
            lblRepairBonepile.Text = FormatNumber(intBonepile, 0)
        End With
        DestroyRepairBonepile()

        'Update the Uncompleted Boards
        lblUncompletedBoards.Text = FormatNumber(Int(lblTotalBoards.Text - lblShippedBoards.Text - lblActiveBoards.Text), 0)

        'Update the Percent Shipped
        lblPercentShipped.Text = FormatPercent(Int(lblShippedBoards.Text) / Int(lblTotalBoards.Text))

        'Update the Percent Active
        lblPercentActive.Text = FormatPercent(Int(lblActiveBoards.Text) / Int(lblTotalBoards.Text))

        'Update the Percent Uncompleted
        lblPercentUncompleted.Text = FormatPercent(Int(lblUncompletedBoards.Text) / Int(lblTotalBoards.Text))
    End Sub
#End Region


Notice that this code doesn't actually call the subroutines to grab the information from the database. It runs a section of the code that exists in the subroutines, used to update the labels and also destroys the DataTable for cleanup:

        With tblTotalBoards
            Dim iRecords As Integer = .Rows.Count()
            For i As Integer = 0 To iRecords - 1
                lblTotalBoards.Text = FormatNumber(.Rows(0).Item(0), 0)
            Next
        End With
        DestroyTotalBoards()


The only reason I can think of that this works, is because it is not actually a part of the BackgroundWorker, but just some code that calls after the BackgroundWorker finishes what it was doing. So the other thread that is created each time the BackgroundWorker is called does the SQL work, and then when it finishes, this is the code that is actually updating my tables. That still doesn't explain why the first code is able to update the labels the first time it is ran, unless my speculation was correct and its due to the thread its on. Can anyone explain this?

Is This A Good Question/Topic? 0
  • +

Replies To: Question about BackgroundWorkers

#2 tlhIn`toq  Icon User is offline

  • Please show what you have already tried when asking a question.
  • member icon

Reputation: 5481
  • View blog
  • Posts: 11,762
  • Joined: 02-June 10

Re: Question about BackgroundWorkers

Posted 14 June 2012 - 05:24 PM

View PostCelerian, on 14 June 2012 - 02:35 PM, said:

I have stepped through the code, and I find that the piece of code that updates the label seems to be where the issue is,


Well, yah... Because your underlying idea is just WAY wrong.

To start with the background worker is a different thread than the thread that created the GUI controls. So you can't update those controls from that backgroundworker. That's why you are getting an error (cross threading). The very idea is just bad and violates the entire idea of having a backgroundworker thread doing works, while keeping your GUI responsive.

The backgroundworker should only do work. Period.

You can subscribe to its WorkDone and ProgressChanged events to update a progress bar and so on.

But it should in no way be responsible for all that you have heaped on it. All that work really needs to be broken down into individual tasks. Do one task: Have those results go to the next task and so on. If you like, each task can be on a thread.
Was This Post Helpful? 0
  • +
  • -

#3 Celerian  Icon User is offline

  • D.I.C Regular


Reputation: 144
  • View blog
  • Posts: 384
  • Joined: 30-March 12

Re: Question about BackgroundWorkers

Posted 15 June 2012 - 05:23 AM

I am not receiving an error though, just no update to the label on subsequent passes under the DoWork code. However, as I stated in the original post, the first time the code steps through with that backgroundworker it does update the labels. In fact, the code works fine when I have all those parts working together. The code for DoWork grabs the initial values and populates the fields. For each subsequent time, it looks like the DoWork thread is just updating the datatables, and then when the RunWorkerCompleted code runs, the labels are updated.

The entire idea for starting a background thread is that when I first started making all these database calls, it would cause the program to hang until all the data was collected and populated. I didn't want the program to be unresponsive, so I started looking into threading. I was just looking for some reasoning behind why it works the way it does, since I'm basically repeating the code in two places and each part works at different times.
Was This Post Helpful? 0
  • +
  • -

#4 tlhIn`toq  Icon User is offline

  • Please show what you have already tried when asking a question.
  • member icon

Reputation: 5481
  • View blog
  • Posts: 11,762
  • Joined: 02-June 10

Re: Question about BackgroundWorkers

Posted 15 June 2012 - 08:07 AM

During this time when the labels are not updating... Is the window otherwise responsive? Or does it seem hung by the intense work going on, similar to when you were single threaded?
Was This Post Helpful? 0
  • +
  • -

#5 Celerian  Icon User is offline

  • D.I.C Regular


Reputation: 144
  • View blog
  • Posts: 384
  • Joined: 30-March 12

Re: Question about BackgroundWorkers

Posted 15 June 2012 - 09:21 AM

It is responsive. The DoWork part updates my tables and gets stuff from the database on every pass through. The labels just don't update unless I have the RunWorkerCompleted code running as well.

This is really the only confusion I have. If the DoWork code can update the labels the first time it runs, why wouldn't it update the labels the subsequent times that it runs. I stated that I had an idea, but wasn't sure, so I was looking for a little clarification.

Ideally, I'd like the background worker to handle this set of calls and label updates whenever the timer controlling it goes off, leaving the main thread to handle other things that are going on (a clock, user clicks). If the RunWorkerCompleted code is necessary for the reoccurring label updates, then I guess I will have to keep it for now (and possibly continue research at a better method. You indicated that what I was doing, while working as intended, goes against what the BackgroundWorker was supposed to be for. This is the first program where I have needed to use more than the one thread that the program originally creates, so it is a learning experience for me.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1