Home All Groups Group Topic Archive Search About

Threading: ThreadStart vs BeginXXX/EndXXX

Author
16 Nov 2004 10:19 AM
AMI

I started with a fairly simple task:  Displaying a progressbar on a WinForm
for a lengthy download over HTTP.  I soon arrived at the question:  What is
the best way to start a worker thread and why?

As I see it, the framework gives us the ThreadStart delegate to do this, and
also the BeginXXX/EndXXX model for asynchronous operations.  The ThreadStart
delegate usage is quite simple to understand.  I've come to understand the
other model, and now my refined question is:  What benefits of going with
the other model?

I wrote some pseudocode below to illustrate the two different approaches to
the same problem, and I can't see why the heck one would choose the latter.
Thanks for any feedback!

------------------
ThreadStart approach
------------------
private void MainMethod()
{
   Thread thread = new Thread(new ThreadStart(this.DownloadFile));
   thread.Start();
   thread.Join();
   MessageBox.Show("I'm done.")
   return;
}

private void DownloadFile()
{
   // Do some work
   // Update progressbar
   // Do more work
   // Update progressbar
}



--------------------------
Asynchronous method approach
--------------------------
private void MainMethod()
{
   IAsyncResult result = BeginDownloadFile(new
AsyncCallback(EndDownloadFile));
   result.WaitHandle.WaitOne();
   MessageBox.Show("I'm done.")
   return;
}

private IAsyncResult BeginDownloadFile(AsyncCallBack callback)
{
   // Set up my own worker thread, point it to DownloadFile using
ThreadStart
   // Somehow register the callback method to a Thread complete event (????)
   // Start my thread
   // Package up my homerolled MyAsyncResult object and return it
}

private void DownloadFile()
{
   // Do some work
   // Update progressbar
   // Do more work
   // Update progressbar
}

private void EndDownloadFile(IAsyncResult result)
{
   ...
}

public class MyAsyncResult : IAsyncResult
{
   ...
}

Author
16 Nov 2004 12:18 PM
Jon Skeet [C# MVP]
AMI <n*@reply.com> wrote:
> I started with a fairly simple task:  Displaying a progressbar on a WinForm
> for a lengthy download over HTTP.  I soon arrived at the question:  What is
> the best way to start a worker thread and why?
>
> As I see it, the framework gives us the ThreadStart delegate to do this, and
> also the BeginXXX/EndXXX model for asynchronous operations.  The ThreadStart
> delegate usage is quite simple to understand.  I've come to understand the
> other model, and now my refined question is:  What benefits of going with
> the other model?
>
> I wrote some pseudocode below to illustrate the two different approaches to
> the same problem, and I can't see why the heck one would choose the latter.
> Thanks for any feedback!

BeginXXX/EndXXX typically *don't* start an extra thread - they use the
ThreadPool instead. Of course you *could* start an extra thread, but if
so you should warn people about this behaviour. You could use a custom
thread pool as well, instead of the system thread pool.

As for which is better, it really depends on how your class is going to
be used. Note that start a new thread and then joining it immediately
is almost always entirely pointless.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Are all your drivers up to date? click for free checkup

Author
16 Nov 2004 6:01 PM
AMI
> BeginXXX/EndXXX typically *don't* start an extra thread - they use the
> ThreadPool instead. Of course you *could* start an extra thread, but if
> so you should warn people about this behaviour. You could use a custom
> thread pool as well, instead of the system thread pool.

Interesting, I didn't know that.  Be that as it may, if I was able to mimic
the ThreadPool behavior of BeginXXX/EndXXX, what then is the difference when
compared to ThreadStart?  I'm still trying to understand why there are two
different models for asynchronous operations, and what the significance of
the difference is.

Given the relative complexity of BeginXXX/EndXXX, is it more a matter of
form practiced by authors of class libraries rather than application
developers?  This too seems odd, since every method has the ability to be
executed asynchronously via the ThreadStart approach.


>Note that start a new thread and then joining it immediately
> is almost always entirely pointless.

One benefit of it would be that the main thread would be available to update
the progress bar, right?  Or does Thread.Join() block so that _nothing_
happens on that thread?


Show quoteHide quote
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MPG.1c04006d63a4221498b8e7@msnews.microsoft.com...
> AMI <n*@reply.com> wrote:
> > I started with a fairly simple task:  Displaying a progressbar on a
WinForm
> > for a lengthy download over HTTP.  I soon arrived at the question:  What
is
> > the best way to start a worker thread and why?
> >
> > As I see it, the framework gives us the ThreadStart delegate to do this,
and
> > also the BeginXXX/EndXXX model for asynchronous operations.  The
ThreadStart
> > delegate usage is quite simple to understand.  I've come to understand
the
> > other model, and now my refined question is:  What benefits of going
with
> > the other model?
> >
> > I wrote some pseudocode below to illustrate the two different approaches
to
> > the same problem, and I can't see why the heck one would choose the
latter.
> > Thanks for any feedback!
>
> BeginXXX/EndXXX typically *don't* start an extra thread - they use the
> ThreadPool instead. Of course you *could* start an extra thread, but if
> so you should warn people about this behaviour. You could use a custom
> thread pool as well, instead of the system thread pool.
>
> As for which is better, it really depends on how your class is going to
> be used. Note that start a new thread and then joining it immediately
> is almost always entirely pointless.
>
> --
> Jon Skeet - <sk***@pobox.com>
> http://www.pobox.com/~skeet
> If replying to the group, please do not mail me too
Author
16 Nov 2004 7:37 PM
Jon Skeet [C# MVP]
AMI <n*@reply.com> wrote:
> > BeginXXX/EndXXX typically *don't* start an extra thread - they use the
> > ThreadPool instead. Of course you *could* start an extra thread, but if
> > so you should warn people about this behaviour. You could use a custom
> > thread pool as well, instead of the system thread pool.
>
> Interesting, I didn't know that.  Be that as it may, if I was able to mimic
> the ThreadPool behavior of BeginXXX/EndXXX, what then is the difference when
> compared to ThreadStart?  I'm still trying to understand why there are two
> different models for asynchronous operations, and what the significance of
> the difference is.

BeginXXX/EndXXX is usually (IME) used for IO operations which don't
actually require a thread to be doing anything until something external
happens.

Sometimes it's more appropriate to use that model - other times you
want to start a whole other thread.

> Given the relative complexity of BeginXXX/EndXXX, is it more a matter of
> form practiced by authors of class libraries rather than application
> developers?  This too seems odd, since every method has the ability to be
> executed asynchronously via the ThreadStart approach.

Yes, it's fairly rare to implement BeginXXX/EndXXX yourself. Don't
forget that you can always use BeginInvoke/EndInvoke on arbitrary
delegates, if you want.

> >Note that start a new thread and then joining it immediately
> > is almost always entirely pointless.
>
> One benefit of it would be that the main thread would be available to update
> the progress bar, right?  Or does Thread.Join() block so that _nothing_
> happens on that thread?

The latter - almost.

See http://blogs.msdn.com/cbrumme/archive/2003/04/17/51361.aspx

It's all a bit difficult, but it's pretty nasty to rely on any pumping
being done during Thread.Join. Treat it as a thoroughly blocking
method.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Author
16 Nov 2004 7:57 PM
AMI
Makes sense, but then I seem to be back at a simple WinForms question, which
is:

What is the correct threading technique for invoking DoLengthyOperation() in
response to a button click (and not wanting my form to freeze up, of
course)?

Show quoteHide quote
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MPG.1c04673c55464d9898b8eb@msnews.microsoft.com...
> AMI <n*@reply.com> wrote:
> > > BeginXXX/EndXXX typically *don't* start an extra thread - they use the
> > > ThreadPool instead. Of course you *could* start an extra thread, but
if
> > > so you should warn people about this behaviour. You could use a custom
> > > thread pool as well, instead of the system thread pool.
> >
> > Interesting, I didn't know that.  Be that as it may, if I was able to
mimic
> > the ThreadPool behavior of BeginXXX/EndXXX, what then is the difference
when
> > compared to ThreadStart?  I'm still trying to understand why there are
two
> > different models for asynchronous operations, and what the significance
of
> > the difference is.
>
> BeginXXX/EndXXX is usually (IME) used for IO operations which don't
> actually require a thread to be doing anything until something external
> happens.
>
> Sometimes it's more appropriate to use that model - other times you
> want to start a whole other thread.
>
> > Given the relative complexity of BeginXXX/EndXXX, is it more a matter of
> > form practiced by authors of class libraries rather than application
> > developers?  This too seems odd, since every method has the ability to
be
> > executed asynchronously via the ThreadStart approach.
>
> Yes, it's fairly rare to implement BeginXXX/EndXXX yourself. Don't
> forget that you can always use BeginInvoke/EndInvoke on arbitrary
> delegates, if you want.
>
> > >Note that start a new thread and then joining it immediately
> > > is almost always entirely pointless.
> >
> > One benefit of it would be that the main thread would be available to
update
> > the progress bar, right?  Or does Thread.Join() block so that _nothing_
> > happens on that thread?
>
> The latter - almost.
>
> See http://blogs.msdn.com/cbrumme/archive/2003/04/17/51361.aspx
>
> It's all a bit difficult, but it's pretty nasty to rely on any pumping
> being done during Thread.Join. Treat it as a thoroughly blocking
> method.
>
> --
> Jon Skeet - <sk***@pobox.com>
> http://www.pobox.com/~skeet
> If replying to the group, please do not mail me too
Author
16 Nov 2004 8:33 PM
Jon Skeet [C# MVP]
AMI <n*@reply.com> wrote:
> Makes sense, but then I seem to be back at a simple WinForms question, which
> is:
>
> What is the correct threading technique for invoking DoLengthyOperation() in
> response to a button click (and not wanting my form to freeze up, of
> course)?

Start a new thread, and get that to call back into the form (using
Invoke or BeginInvoke) when it's finished.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Author
16 Nov 2004 1:35 PM
John Saunders
"AMI" <n*@reply.com> wrote in message news:Sukmd.92656$HA.63967@attbi_s01...
>I started with a fairly simple task:  Displaying a progressbar on a WinForm
> for a lengthy download over HTTP.  I soon arrived at the question:  What
> is
> the best way to start a worker thread and why?

....

There's a better question, and perhaps you already know the answer:

....

> private void DownloadFile()
> {
>   // Do some work
>   // Update progressbar
>   // Do more work
>   // Update progressbar
> }

Perhaps you know that you can't directly update the progress bar from
another thread? In fact, you can't directly touch any part of the UI from
another thread. Instead, you need to use Control.Invoke to marshall all of
your UI work to the UI thread:

private void UpdateProgressBar(ProgressBar bar, int progressValue)
{
    bar.Value = progressValue;
}

private delegate void ProgressBarHandler(ProgressBar bar, int
progressValue);

private void DownLoadFile()
{
    // Do some work
    // Update progressbar
    progressBar.Invoke(new ProgressBarHandler(UpdateProgressBar), new
object[]{progressValue});
    // Do more work
    // Update progressbar
    progressBar.Invoke(new ProgressBarHandler(UpdateProgressBar), new
object[]{progressValue});
}

of course, you can get fancy and hide all the Invoking inside of
UpdateProgressBar:

private void UpdateProgressBar(ProgressBar bar, int progressValue)
{
    if (bar.InvokeRequired)
    {
        bar.Invoke(new ProgressBarHandler(UpdateProgressBar), new
object[]{progressValue});
    }
    else
    {
        bar.Value = progressValue;
    }
}


You probably knew this, but other readers might not have known...

John Saunders

P.S. I learned the hard way that this rule also applies to modifying objects
which might raise events to the UI. I once had an application in which the
main form displayed a DataGrid. The DataGrid was bound to a DataSet. The
DataSet was being updated in a background thread.

Bad Idea, as the DataGrid had created a DataView against the DataSet, and
was listening to the ListChanged event of that DataView. My innocent
addition of a row raised ListChanged - directly from my background thread to
the DataGrid. Not a pretty sight.

Bookmark and Share