views:

395

answers:

5

I want to start a job in a new thread or using backgroundworker to do it but havent done that before and asking you wich way I should do it.

My program has a datagridview with a list of files, one file per row. I want the user to be able to select a row and then press "Start download" to start a background job of the download. I want to get events back of the progress of the download.

I have a class clsDownload that handles everything and raises events back but how do I implement the backgroundworking?

Should I use the System.ComponentModel.BackgroundWorker inside of the class or create some wrapper that handles this or use some other threading stuff?

Thanks.

Edit: I dont understand how to implement my download in the backgroundworker, any small example would be very nice. The example on msdn didnt get me far.

I have a download class that has a StartDownload-function. Should I use the backgroundworker IN the class or in the caller? "feeling stupid"

A: 

The backgroundworker looks like it should work... There is an example on MSDN.

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

Or you could do something like:

WaitCallBack workCallBack= new WaitCallBack(DownloadMethod);
if(!ThreadPool.QueueUserWorkItem(workCallBack, "ThreadPooled")
{
   // Unable to Pool
}

// Work has been added to pool and will execute when possible

Depends what parameters if any you need for the Thread.

Ian
I'd advise against WaitCallback since it's harder to return events to the UI thread (which I assume is something he wants to accomplish).
Inferis
+3  A: 

I you are only going to do downloading and don't need other async processing, you can just use the async methods of the WebClient class. Though since you already have your own class, that's probably not a solution for you.

Otherwise, you can use BackgroundWorker as you mentioned. The MSDN page has an example how to do it.

EDIT: The short story is:

  • You create the BackgroundWorker from the caller;
  • When you want to start the background work, you call BackgroundWorker.RunWorkerAsync;
  • in the DoWork event handler you do the background work, in your case you start your download class;
  • while you're doing the background work, you have to check every once in a while for CancelationPending;
  • when you want to report some progress, you need to calculate it in percentage and call ReportProgress.

And if you need something really customized, you can always create your own Thread.

I personally would stick with BackgroundWorker. It has a nice set of notifications for various stages of the job. If you use Thread, you will have to implement these youself.

I would also make sure the code does not create too many instances. You want to limit the number of concurent downloads and queue anything past that number.

Franci Penov
Im using webservices and MTOM to get large files from a server so I cant co with WebClient.
Stefan
In that case, generate async methods on your service reference and uses these.
Inferis
@inferis - async methods on the webservice will not provide notifications on progress.
Franci Penov
True. Besides, he has a class around it already, so `BGW` is the way to go.
Inferis
My class reads chunks of file in a loop until the whole file is downloaded. I raise an DownloadedBytes-event after every chunk readed. Now, how to use the backgroundworker, I dont really understand where to use it, in the caller or in the download class.
Stefan
+3  A: 

I'd strongly advise BackgroundWorker if you need to provide feedback to the user on the UI. The ProgressChanged and RunWorkerCompleted events are run on the UI thread, so there's no need to do marshalling, which can be a pain in the ass (not to mention that it makes your code more complex).

Inferis
+1  A: 

I've created several different classes that incorporate BackgroundWorker. What I generally do is have a BackgroundWorker component on the form that will be open when the job is being performed, then I pass that instance to the constructor of my job class.

Here is what your job class might look like:

Private m_bwMain As BackgroundWorker

Public Sub New(ByVal bwMain As BackgroundWorker)
    m_bwMain = bwMain

    'additional setup code here
End Sub

To start a job, you would do something like this in the Click event handler of your Start Download button:

lblStatus.Text = "Initializing ..."
bgwMain.RunWorkerAsync(someFileName)

I declare my job class as a private member of the current form, then instantiate it in the BackgroundWorker.DoWork event. From there you can call your method to download a file:

Private Sub bgwMain_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwMain.DoWork
    m_oJobEngine = New JobEngine(CType(sender, BackgroundWorker))
    m_oJobEngine.DownloadFile(CStr(e.Argument))
End Sub

To report progress to the user, you can handle the events raised by your class in your main form. You just need to make sure the job class object declaration has the WithEvents keyword. From those handlers you can call the ReportProgress method of BackgroundWorker. From within ReportProgress you can make whatever changes you need to the UI to indicate progress. Here's an example:

Private Sub m_oJobEngine.DownloadProgress(ByVal bgw as Backgroundworker, ByVal bytesTransferred as Long) Handles m_oJobEngine.DownloadProgress
    bgw.ReportProgress(0, bytesTransferred)
End Sub
Private Sub bgwMain_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bgwMain.ProgressChanged
    lblStatus.Text = CLng(e.UserState).ToString & " bytes transferred."
End Sub

Hope this helps.

Chris Tybur
A: 

The class that uses clsDownload (probably your Form class) should use BackgroundWorker to run the download method.

mbeckish