views:

860

answers:

6

My app monitors a directory where users can upload a file. When a new file is detected it is added to a queue. I have a timer that runs through the queue and determines if the file has finished uploading. If there are any files that are complete it will take the most recent and begin running a background task (using BackgroundWorker).

My problem is that I don't know how to handle the timer while the background task is running. For example, if the timer is set to 10 seconds and the background worker is still working I want the timer to skip execution of another background worker until its next iteration.

Also, the information for each task is stored in the Queue (a ListView control) and I pass the ListViewItem to the background worker. I'm curious if passing the ListViewItem has any side effects.

Thanks!

+1  A: 

You could store ready-to-process files in another queue (like a Queue< string> ) and have the BgWorker continuously poll that Queue. You might get better performance too, less idle time. You will have to protect the Queue (with Monitor) and have the BgWorker use Monitor.Wait when the Queue is empty.

To get an idea, look for Marc Gravell's answer on this question.

Henk Holterman
I ended up following your advice and used a separate queue to process files that were "ready."
DennyDotNet
A: 

Really hard to answer this without seeing the code you are talking about. However, if you have to synchronize multiple asynchronous events (detecting a file is downloaded, adding the file to a queue, processing the queue) I suggest creating a single BackgroundWorker that does all tasks. Then it is easy in your worker to test what state each step is at. I would avoid creating multiple threads and attempting to synchronize them, this is very very problematic.

I would also not store any background task information in a UI data structure (like a ListView control). Create a callback or event in your BackgroundWorker that will alert the UI when it needs to display something.

Dour High Arch
You are right about not passing around the UI data structure, it was a bad idea in my initial concept. I decided to use Henk's advice and store things in a queue once they were ready. However your answer was also helpful. Thanks
DennyDotNet
A: 

Is the BackgroundWorker.IsBusy Property what you're looking for?

Dennis Palmer
This is what I was initially looking for but I decided to refactor my code a bit so that I wasn't dependent on passing around a ListViewItem. Thanks
DennyDotNet
A: 

The simplest thing you could do is do all the work (including checking the folder) inside your BackgroundWorker: Check if you have something to do, if yes, do it, if not, use Sleep(time) or WaitOne(time) to suspend the thread for some time.

I don't think you need a thread-safe queue here, because folder is being updated asynchronously anyway. So you only need one thread, but you need a way to stop it. That is why AutoResetEvent.WaitOne(time) would be better than Sleep(time) - you can signal the event from the main thread to end your background worker earlier.

Groo
A: 

On your Timer.Tick handler, check the BackgroundWorker.IsBusy property to determine if it's ready for another bit of work or not. If not, just skip giving it the work and wait until the next Tick.

JP Alioto
A: 

You can create a thread queue in which you place work-to-do. Your background worker loops around pulling off items from the queue and perform the work. Some things need to be considered:

  • The queue needs to be thread safe, and quite likely you'd want the backgroundworker to block if the queue is empty, and wake up when an item becomes available. Im sure somone has made such a nice queue already.
  • The items you post to the queue will be operated on in another thread(the backgroundworker). Make sure this is done in a thread safe manner (e.g. don't post items to the queue that both the main application and the background worker will alter)

Another and easier approach is to

  • Queue up items in your application. Kick off the backgroundworker one first time.
  • When you get the event that the backgroundworker is done, pick the next item off the queue and start the backgroundworker again with this item.
  • You still need to care about thread safety. When you've sent an item to the backgroundworker, make sure only the backgroundworker operates on it(e.g. if you're just sending it strings, send it a copy of said string instead)

I didn't quite follow you on the timer. If the background worker is finished you should get an event and you'll know it's finished, no need for a timer to check this.

nos