1

I have read the answers from How to wait for a BackgroundWorker to cancel?, but I couldn't find a solution to my specific problem:

My app has to load a large amount of data, but in most cases this data won't be required immediately after the app has started.

To minimize the delay when the user actually requests the data, I load it using a BackgroundWorker, which launches when the app starts. Hopefully, when the user requests the data, the BackgroundWorker has completed.

In some cases, however, it might not have. In these cases I want to wait for the loading to complete before showing anything to the user.

All the techniques I can think of have race conditions: for example, if I set up an AutoResetEvent, I can't use WaitOne() when the user requests the data, because the AutoResetEvent might already have signaled; if I add a boolean loading_complete flag and check it before calling WaitOne, loading_complete might be set to true after the ckecing, but before the WaitOne call, which will never return...

Any idea?

EDIT: Thanks to @500-InternalServerError for the solution; using a ManualResetEvent works great. Thanks to everyone else for the suggestions.

Community
  • 1
  • 1
Clément
  • 12,299
  • 15
  • 75
  • 115
  • If you use .net 4.0 or above, you can use `Task`. – Hamlet Hakobyan Dec 14 '12 at 23:03
  • @HamletHakobyan Unfortunately I'm on SilverLight for Windows Phone, which doesn't have `Task`... – Clément Dec 14 '12 at 23:18
  • 1
    Well, just enable a button in the RunWorkerCompleted event. – Hans Passant Dec 14 '12 at 23:20
  • @HansPassant That doesn't do either: given that I'm on a phone it takes a small but noticeable time to compose the UI for the visualization window; I would like to let the user open the visualization, and display a message until the data is available. – Clément Dec 14 '12 at 23:24
  • Erm, hide the message then. Not sure why any of this needs to be difficult, sorry. – Hans Passant Dec 14 '12 at 23:31
  • 3
    Just use a manual reset event that you reset before starting the background worker and then wait on in the main thread. The background worker then sets this when it's done to wake up the main thread. – 500 - Internal Server Error Dec 14 '12 at 23:44
  • @500-InternalServerError: That would freeze the main thread, which also happens to be the UI thread, wouldn't it? – Clément Dec 14 '12 at 23:46
  • @HansPassant: I'm sorry, I don't get it either; perhaps I missed something? My point is that if I disable the button until the data is loaded when clicking it the user will have to wait for an extra .3 seconds for the UI to render, while the UI could have rendered while the data was loading. – Clément Dec 14 '12 at 23:48
  • @Clément: Absolutely - I thought that was the point: That the main thread eventually reached a point where it could not go on without the data from the background thread. If the main thread just wants to poll if the data is ready it can use a wait with a timeout. – 500 - Internal Server Error Dec 14 '12 at 23:50
  • @500-InternalServerError: The problem is that if I use a manual reset and call WaitOne while Set has already been called, it will never return, will it? – Clément Dec 14 '12 at 23:52
  • 1
    Wait will return immediately in that case, which should be what you want. – 500 - Internal Server Error Dec 14 '12 at 23:53
  • @500-InternalServerError: You're quite right indeed! Thanks a lot for the help; if you add this as an answer I'll gladly accept it. Thanks again! – Clément Dec 15 '12 at 00:14

4 Answers4

0

You put whatever should happen once the BackgroundWorker is done in the WorkCompleted event handler

Louis Kottmann
  • 16,268
  • 4
  • 64
  • 88
  • Nothing should happen when the BackgroundWorker completes, at least nothing that I know of when I launch said BackgroundWorker: I only use the data it loads after – Clément Dec 14 '12 at 23:07
  • I think that perhaps Baboon means that BackgroundWorker should trigger that event, and all other threads should wait on this event before resuming their work. – didierc Dec 14 '12 at 23:11
  • @didierc: I understand, but I have no way of knowing whether the event has already fired or not when I enter the part of the app which requires the data to have fully loaded. in other words, when I get to the point where I need to wait for the data, the WorkCompleted event might or might not have fired yet, and if I use a boolean flag to check I'll run into race conditions. – Clément Dec 14 '12 at 23:20
  • @didierc Then you need to rethink how you do it, if you need the bgw to be done but can't wait for him to say "I'm done" then you have architectural issues. – Louis Kottmann Dec 14 '12 at 23:55
  • @Clément I meant that for you, not didierc ;) – Louis Kottmann Dec 15 '12 at 11:32
  • @Baboon: I can wait for the background worker to be done; it's just that sometimes I'll need the data long after the bgw has finished, and sometimes before; in the second case I want to wait; in the first case, the WorkCompleted event will already have fired when I retrieve the data. – Clément Dec 15 '12 at 11:38
0

Use a simple mutex locked by BackgroundWorker, and have any other threads waiting for completion to acquire and release it. Once BackgroundWorker is done, have it release the lock, and all other threads should be able to continue their work.

There might be some specific C# way of doing this (I think the Monitor class might be handy).

Update: In fact, the object needed to solve the problem is a form of condition variable working as a latch, and, as mentioned by OP, ManualResetEvent covers that specific need.

didierc
  • 14,572
  • 3
  • 32
  • 52
0

Background worker supports RunWorkerCompleted event. This event can be used to determine if the loading has finished or not.

There are two conditions

  • User has requested data after the worker has completed.

To make the things less complicated, you can add a boolean member variable which will be set to false when the background worker starts loading data. Once the data is loaded, the runworkercompleted event will fire which will set the variable to true which will help you determine if the data is loaded or not.

  • User has requested data before worker is completed (can be determined from the above stated bool variable) and waiting for the data to load.

When the user request the data, you can set a flag/another bool variable such as

WaitingForData = true;

when the RunWorkerCompleted event is fired, it will check for the status of WaitingForData and if it is true, it will display the data. This way, you will not need to wait for the thread completion.

Using this method will help you avoid any race condition or inter thread communication.

Murtuza Kabul
  • 6,438
  • 6
  • 27
  • 34
  • This sounds like a good idea, but it doesn't account for the fact that there are multiple data visualisation pages in my app, and the BackgroundWorker won't know which to send the data to... @500-InternalServerError's solution works well, though. – Clément Dec 15 '12 at 09:35
  • If you have multiple components showing the data, you can consume the event at multiple places. – Murtuza Kabul Dec 15 '12 at 10:24
  • Given the cost of UI layout, rendering all UI elements with data is not feasible, given that only one will be shown in the end. – Clément Dec 15 '12 at 11:40
  • May be I am unable to understand what you are exactly trying to achieve but I smell architectural issues with your application. Such a situation should not happen in the first place. If you explain a bit more, I can help you out with highly efficient solution for your problem. – Murtuza Kabul Dec 15 '12 at 12:10
  • Thanks, but the problem is pretty much solved on my side. @500-InternalServerError's works perfectly. – Clément Dec 15 '12 at 15:01
-1

Use a boolean flag, set it to true in the background worker completed event handler.

When you need to check whether the data is loaded just check the variable.

Make sure you lock it before trying to access it to avoid the race condition.

If the data is not ready when it is requested, you could add another event handler at that point to the background worker completed event, which does what needs to be done with the data.

PeteGO
  • 5,597
  • 3
  • 39
  • 70