Link to home
Start Free TrialLog in
Avatar of Steve Marshall
Steve MarshallFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Main UI Freeze With BackgroundWorker

Hi all, hope you can help. I am having a problem that I was not expecting (based on my readings), so it seems clear that I have missed something.

I have a main form in an application that displays a Work Schedule for a machine. Periodically a legacy application (that I cannot change) updates the machine schedules meaning that the display on the screens needs to update. The application running on the machine screens has a Timer that fires every 10 minutes to query the schedule database to get the current Work Schedule for the particular machine.

The code on the Timer Tick event is simplicity itself:

Private Sub tmrRefreshJobs_Tick(sender As Object, e As EventArgs) Handles tmrRefreshJobs.Tick
    
    bgwRefreshJobs.RunWorkerAsync()

End Sub

Open in new window


It just fires up a BackgroundWorker that goes off and queries the database for the current Work Schedule for the machine. The DoWork event is no more complicated, simply calling a routine to query the database and return a list of jobs. All of the work is done in that routine, and even that is straight-forward - it uses a DataTable and DataAdapter to query data from a SQL database and load it to the list.

Private Sub bgwRefreshJobs_DoWork(sender As Object, e As DoWorkEventArgs) Handles bgwRefreshJobs.DoWork

    Dim newPlannedJobs As List(Of MachineJob) = PlannedJobsForMachine()

End Sub

Open in new window


I was expecting the Main UI to remain responsive, but I have had reports of the screen "freezing" for a few seconds. I added a simple clock to the screen to show the current time in HH:MM:SS format, and when the BGW queries the database the UI does in fact freeze for 4s consistently each time it refreshes. It is the line above in the DoWork event that is freezing the UI.

I have removed additional code from the DoWork that processes the returned list and using the ReportProgress updates the display - but the removed code runs in the blink of an eye and is not the issue.

I was of the belief that the BGW runs on a seperate thread, and therefore the querying of the database should not freeze the UI as the work was on a seperate thread.

What am I missing?

Steve
Avatar of Éric Moreau
Éric Moreau
Flag of Canada image

The BackgroundWorker class does not do magic. At some points code needs to run.

Any your PlannedJobsForMachine method is surely running regular code (as opposed to task).

Have a look at https://www.emoreau.com/Entries/Articles/2013/11/Async-and-Await-in-Net.aspx and https://www.emoreau.com/Entries/Articles/2013/10/SystemThreadingTask-class.aspx
ASKER CERTIFIED SOLUTION
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Steve Marshall

ASKER

Hi all.

The comment made by AndyAinscow above got me to thinking. It is clearly and widely documented that a background thread cannot access the controls created on the Main UI thread. Maybe there is a less documented "feature" that a background thread, although it can access "data objects" created on the Main UI thread and execute code routines within those objects (as long as they do not access UI controls), the code of that object actually executes on the thread that created it - i.e. the Main UI thread. It was a thought, and it would explain what I was seeing in my application. Maybe this is clearly documented and I have just missed or misunderstood it.

The PlannedJobsForMachine() function that my BackgroundWorker calls is defined in a Data Service object (object serving up Data Transfer Objects) that is created on the Main UI thread as part of the application startup. My BackgroundWorker calls to that Data Service to get a refreshed list of planned jobs. When it does so it blocks the Main UI thread whilst the data retrieval executes (Data Service object created on Main UI thread, and its code routines must be executing on that thread as it blocks). It is only for 2-3 seconds, but it is there - consistently.

As a test, I changed the BackgroundWorker so that it did not use the Data Service created by the Main UI thread, instead creating a new instance of the object for itself and executing the code in that instance. It no longer blocks the Main UI thread, so although a background thread can access data objects created in another thread and can execute code that are part of those objects, the code does not execute in the background thread.

It would certainly seem that code that is part of an object executes on the thread that creates it, even when executed from another thread. I am not marking this as the solution just yet as I am going to carry out a little more testing, but this seems to have solved the issue that I am seeing.

So, thanks to  AndyAinscow, your two line comment pointed me in the right direction.

Steve
Thanks for the pointers.