Asynchronicity for newbs

R

Robert Morley

As you might guess from the title, I'm a C# newbie, though certainly not a
programming newbie. What I'm trying to set up is basically an asynchronous
class, but I'm a little lost as to which method is appropriate and how to
set it up.

From what I've seen, using the BeginInvoke style of converting a
synchronous call to asynchronous isn't going to work here, but maybe I'm
just doing it wrong.

So for simplicity's sake, let's say I've got a class that has the following:

* a StatusUpdate event that returns a status string to listeners
* a Stop method
* an asynchronous DoSomething method that loops infinitely, updating the
status with each iteration until the Stop method is called.

That would be used by a form that basically calls DoSomething, updates a
text box with the string returned from SatusUpdate whenever the event is
raised, and a Stop button to stop whenever the user decides.

Actual code for the above would be REALLY nice, of course, but even if
someone could just point me in the right general direction of the concepts I
need to look at, it'd be appreciated.


Thanks,
Rob
 
P

Peter Duniho

Robert said:
As you might guess from the title, I'm a C# newbie, though certainly not
a programming newbie. What I'm trying to set up is basically an
asynchronous class, but I'm a little lost as to which method is
appropriate and how to set it up.

From what I've seen, using the BeginInvoke style of converting a
synchronous call to asynchronous isn't going to work here, but maybe I'm
just doing it wrong.

For a true "asynchronous operation" scenario, using the
Delegate.BeginInvoke() method is in fact one of the simplest ways to get
things working. But a) from your description you don't really seem to
have exactly that scenario, and b) it's possible you're seeing the
Control.BeginInvoke() method instance and that's confusing things.
So for simplicity's sake, let's say I've got a class that has the
following:

* a StatusUpdate event that returns a status string to listeners

Easy, but has very little to do with the asynchronous aspect, other than
the event handler may need special logic to deal with
cross-thread/synchronization issues.
* a Stop method
Okay.

* an asynchronous DoSomething method that loops infinitely, updating the
status with each iteration until the Stop method is called.

How often is the status updated? Is the method something that would
loop infinitely normally? Or is the work represented in some other
class, as a single one-shot (non-looping) operation?

Barring any other information, your scenario seems to call for the use
of some kind of Timer class. If you're writing a Forms application,
then the System.Windows.Forms.Timer class is probably most appropriate.
Otherwise, you may want to use System.Threading.Timer instead.

One significant advantage to using the System.Windows.Forms.Timer class,
if you can, is that it raises the Tick event on the main GUI thread,
which not only avoids the need to deal with the Control.Invoke() method,
also essentially keeps all the related code single-threaded, so you also
don't wind up needing to deal with thread synchronization issues.
That would be used by a form that basically calls DoSomething, updates a
text box with the string returned from SatusUpdate whenever the event is
raised, and a Stop button to stop whenever the user decides.

If you use a Timer class, then the above amounts to doing this:

– Create the Timer instance
– Set the Timer up to execute the code that would be in the
DoSomething() method's loop each time the timer fires
– Raise the event as desired in that code
– Stop the Timer when the Stop button is clicked
Actual code for the above would be REALLY nice, of course, but even if
someone could just point me in the right general direction of the
concepts I need to look at, it'd be appreciated.

If you are more specific about your question, you can get more specific
information in the answer to your question. :)

In the meantime, here are a few possible implementation examples that
may or may not be applicable to your specific scenario (caveat:
incomplete, uncompiled, untested code)...


Plain, threading example:

class Form1 : Form
{
Class1 _class1;

void buttonStart_Click(object sender, EventArgs e)
{
_class1 = new Class1();

_class1.StatusUpdate += class1_StatusUpdate;

new Thread(_class1.DoSomething).Start();

buttonStart.Enabled = false;
buttonStop.Enabled = true;
}

void buttonStop_Click(object sender, EventArgs e)
{
_class1.Stop();

buttonStart.Enabled = true;
buttonStop.Enabled = false;
}

void class1_StatusUpdate(string str)
{
label1.Invoke(_UpdateLabel, new object[] { str });
}

void _UpdateLabel(string str)
{
label1.Text = str;
}
}

class Class1
{
public void DoSomething()
{
int i = 0;

while (!Done)
{
Thread.Sleep(250);
Action(i++.ToString());
}
}

public void Stop()
{
Done = true;
}

public bool Done
{
get { return _fDone; }
private set { _fDone = value; }
}
volatile bool _fDone;

public event Action<string> StatusUpdate = delegate { };
}


Plain, threading example, where the actual work involves a non-looping
class:

class Form1 : Form
{
void buttonStart_Click(object sender, EventArgs e)
{
_fDone = false;
new Thread(DoSomethingLoop).Start();

buttonStart.Enabled = false;
buttonStop.Enabled = true;
}

void DoSomethingLoop()
{
Class1 class1 = new Class1();
int i = 0;

while (!_fDone)
{
Thread.Sleep(250);
class1_StatusUpdate(class1.DoSomething(i++));
}
}
volatile bool _fDone;

void buttonStop_Click(object sender, EventArgs e)
{
_fDone = true;

buttonStart.Enabled = true;
buttonStop.Enabled = false;
}

void class1_StatusUpdate(string str)
{
label1.Invoke(_UpdateLabel, str);
}

void _UpdateLabel(string str)
{
label1.Text = str;
}
}

class Class1
{
// Just a simple operation returning a string
public string DoSomething(int i)
{
return i.ToString();
}
}


Timer-based example:

class Form1 : Form
{
System.Windows.Forms.Timer _timer = new System.Windows.Forms.Timer();

void buttonStart_Click(object sender, EventArgs e)
{
Class1 class1 = new Class1();
int i = 0;

// Tick event handler is executed on main GUI thread,
// so no call to Control.Invoke() is needed in order
// to update the label1.Text property:

_timer.Tick += (sender, e) =>
{
label1.Text = class1.DoSomething(i++);
};
_timer.Interval = 250;
_timer.Start();

buttonStart.Enabled = false;
buttonStop.Enabled = true;
}

void buttonStop_Click(object sender, EventArgs e)
{
_timer.Stop();

buttonStart.Enabled = true;
buttonStop.Enabled = false;
}
}

class Class1
{
// Just a simple operation returning a string
public string DoSomething(int i)
{
return i.ToString();
}
}


That last example has a little bit of sneakiness in it. In particular,
this section of code:

Class1 class1 = new Class1();
int i = 0;

_timer.Tick += (sender, e) =>
{
label1.Text = class1.DoSomething(i++);
};

…hides some fairly elaborate compiler-generated code. The first sneaky
bit is the "(sender, e) => { ... }" part. This is a lambda expression
with a statement body, used to declare an anonymous method. Rather than
having a named method to subscribe to the Tick event as the handler,
this unnamed method is used instead.

The next bit is that the variables "class1" and "i" act syntactically as
local variables but in reality, because they are used within the
anonymous method, become "captured" along with the anonymous method.
This means that the lifetime of those variables is extended to that of
the anonymous method instance, so the variables continue to exist even
after the method where they are declared returns.

This allows them to maintain state necessary for the Tick event handler.
Specifically, the Class1 instance reference, along with the counter.

In the first two examples, I avoided using any anonymous methods (*)
because they can sometimes confuse people new to C#. But IMHO they are
a really useful and important thing to learn, because they allow all
related code and variables to remain together in the same declared
method. This helps the code to be more readable, and more concise at
the same time.

Both of the first two examples could also very easily take advantage of
anonymous methods, by using them for the StatusUpdate event handler (in
the first example), and for the thread entry point method and delegate
used for the Control.Invoke() method (in both examples).

If none of this seems to be addressing what you are trying to do, try
posting a less vague question, preferably one that includes code itself.

Pete

(*) (Actually, I lied. There is in fact an anonymous method in the
first code example. See if you can find it! :) )
 
R

Robert Morley

Peter said:
For a true "asynchronous operation" scenario, using the
Delegate.BeginInvoke() method is in fact one of the simplest ways to get
things working. But a) from your description you don't really seem to
have exactly that scenario, and b) it's possible you're seeing the
Control.BeginInvoke() method instance and that's confusing things.

I'll have to go over this all tomorrow, but just a few quick answers to some
of your questions...

A timer might make a lot of sense here, I just wasn't sure if that was the
best approach. Essentially, I'm implementing a job queue with a few seconds
of delay between jobs (which is why I say a timer would make a lot of
sense). Now, when you have 20 jobs firing every few seconds, it's not
terribly onerous...you run your program and just wait until it's done. When
you have several thousand jobs, it's a bit more of a concern that you be
able to start and stop the queue at will.

The main problem I was having was getting the nice, simple Stop button to
work with my synchronous routine. The minute you start something
synchronous, of course, you can't click on the buttons on the form any more.
So I tried switching the routine to asynchronous through BeginInvoke, and
instead started getting cross-thread exceptions being thrown.

The one thing I dislike about timers, at least as I've previously
implemented them (coming from VB6), is that your code gets really spread
out...instead of having a single routine to handle the entire operation, you
end up with three or four routines spread out across your program. Then if
I build different timers for different types of jobs, the routines start
multiplying like rabbits. :) Nevertheless, despite that nuisance, you may
be right and this might be the way to go, at least for now. (Eventually I'd
like to turn this into a robust framework where multiple unrelated jobs can
be queued up, but this is my first C# app beyond "Hello world", so let's
start simple.)
How often is the status updated? Is the method something that would
loop infinitely normally? Or is the work represented in some other
class, as a single one-shot (non-looping) operation?

As mentioned above, in reality, it's looping but non-infinite. The idea is
to launch a routine that'll perform a specific set of operations that can be
started, paused, stopped, and resumed at need.
Barring any other information, your scenario seems to call for the use
of some kind of Timer class. If you're writing a Forms application,
then the System.Windows.Forms.Timer class is probably most appropriate.
Otherwise, you may want to use System.Threading.Timer instead.

I'll look at the System.Threading.Timer, as I'd like to decouple the code
from the form so it's more versatile.
(*) (Actually, I lied. There is in fact an anonymous method in the
first code example. See if you can find it! :) )

First, I'd have to understand what those are. I think I've run across the
concept before, but I'll have to do some reading...definitely not tonight,
though. I'll have a look at your code tomorrow and get back to you.

Thanks for the help so far!


Rob
 
P

Peter Duniho

Robert said:
[...]
A timer might make a lot of sense here, I just wasn't sure if that was
the best approach. Essentially, I'm implementing a job queue with a few
seconds of delay between jobs (which is why I say a timer would make a
lot of sense). Now, when you have 20 jobs firing every few seconds,
it's not terribly onerous...you run your program and just wait until
it's done. When you have several thousand jobs, it's a bit more of a
concern that you be able to start and stop the queue at will.

It's not really clear from your posts what the source of the "every few
seconds" is. Do you specifically need nothing to happen for a few
seconds at a time? Or is it that the tasks take a few seconds each to run?
The main problem I was having was getting the nice, simple Stop button
to work with my synchronous routine. The minute you start something
synchronous, of course, you can't click on the buttons on the form any
more.

Right. And if the answer to my previous question is that the tasks
themselves take a few seconds each to run, you would not actually need
to use a Timer, nor would you want to use the Timer from the Forms
namespace (because all that would do is execute each few-second task on
the main GUI thread, blocking it while each runs.
So I tried switching the routine to asynchronous through
BeginInvoke, and instead started getting cross-thread exceptions being
thrown.

Well, that's the standard error when you try to access GUI controls from
the wrong thread. See the documentation for Control.Invoke() for more
details. The basic issue is that code that accesses members of a
Control instance has to be executed on the same thread that owns that
Control instance. The Control.Invoke() and Control.BeginInvoke()
methods are ways to accomplish that. You pass them a delegate, and they
invoke the delegate on the thread owning the Control instance used to
call the Invoke() or BeginInvoke() method (again, not to be confused
with the Delegate.BeginInvoke() method).
The one thing I dislike about timers, at least as I've previously
implemented them (coming from VB6), is that your code gets really spread
out...instead of having a single routine to handle the entire operation,
you end up with three or four routines spread out across your program.

I don't know why even in VB6 that would be the case. But for sure,
there's no reason that should be the case in C#, VB.NET, or any other of
the .NET languages. Any of the Timers simply have an event you handle,
and it takes just a single event handler to manage that. If you have
multiple methods, it's because that single event handler winds up
calling multiple methods.
Then if I build different timers for different types of jobs, the
routines start multiplying like rabbits. :)

You should not need to build any timers, never mind different ones for
different types of jobs. You should be able to use the timer classes
built into .NET.
Nevertheless, despite that
nuisance, you may be right and this might be the way to go, at least for
now.

Well, like I said. I can't really tell from the question at this point
whether a timer is useful or not. It depends on whether you're trying
to impose a delay, or the delay is imposed on you unavoidably.

Pete
 
R

Robert Morley

This is getting long, and probably very specific to me at this point, rather
than the whole newsgroup, so I'll respond privately if you don't mind.


Thanks,
Rob
 
P

Peter Duniho

Robert said:
This is getting long, and probably very specific to me at this point,
rather than the whole newsgroup, so I'll respond privately if you don't
mind.

Sorry, and no offense intended, but yes…I do mind.

I can elaborate on the reasons if you like, but the basics are: I (and
everyone else here answering questions) do this for free, and a large
part of the motivation is that because the discussions take place in
public, answers provided to just one person can in fact be useful for a
much larger audience.

If your code is too specific to your own implementation for you to think
it's worth posting here, or too long to post here, then you probably
have not distilled the basics of your problem down to a true
concise-but-complete code example suitable for the newsgroup.

In-depth work one-on-one is the type of thing paid consultants do. I
view my contributions here as more of a community-building exercise, and
definitely not a paid consultant job. :)

If you would like to receive help via this free forum, please do what
you need to in order to present your question in a general enough way
that an answer would be useful to anyone else working on something
similar, and yet still specific enough for you to receive the answer you
need. And then, of course, post it here rather than emailing it. :)

Pete
 
R

Robert Morley

Peter said:
Sorry, and no offense intended, but yes…I do mind.

Fair enough. The timer example worked out quite well, as did the
Control.Invoke suggestion, so I think we're good in any event.


Rob
 
R

Robert Morley

Robert said:
Fair enough. The timer example worked out quite well, as did the

Sorry, I meant to say "timer suggestion". In point of fact, I used the
System.Threading.Timer class instead of the Windows Forms one.

Re-looking at your code, it seems I might be able to further reduce a few
things in my own, but I still haven't wrapped my mind around a lot of the
concepts that VB6 either didn't have or weren't commonly used, so it'll take
a bit of playing around to figure out what's going to work and what won't.

Thanks again for the help.


Rob
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top