Q: how to make service OnStop wait long enough?

M

Matt C.

This is my first attempt at writing a .NET service, and also my first
attempt at using threads. I don't know what I'm doing.

Below is some simplified code from my service class (inheriting from
ServiceBase). The OnStart calls a loop, which continues forever on its own
thread as long as _keepProcessing is true. This seems to work and I'm happy
with it.

However, OnStop is not working quite the way I would like. I would like
OnStop to set _keepProcessing to false, which should then cause the loop to
exit and that thread to terminate normally, thereby allowing OnStop to
finish normally as well. (Right?) But if _loopThread does not terminate
fast enough to suit OnStop (about a minute), a message pops up, "the service
didn't respond to the control request in a timely fashion" and then the
entire process is killed anyway. (!!!) This makes me uncomfortable.

Two questions:

1) Is my general approach below sensible, or is there a better way to
achieve the same basic effect?

2) Is there some way to make OnStop wait until the loop is really done
rather than just giving up waiting and killing the process?

Thanks for any advice,
Matt

-------------------------

private static bool _keepProcessing;
private static Thread _loopThread;

private static void AutomatedProcessingMain()
{
bool isTimeToDoStuff;

while(_keepProcessing)
{
try
{
if (isTimeToDoStuff == true) //setting iTTDS omitted here
DoStuff();
else
System.Threading.Thread.Sleep(15000);
}
catch (Exception x)
{
LogErrAndContinue(x);
}
}
}

static void Main()
{
System.ServiceProcess.ServiceBase[] ServicesToRun;

ServicesToRun = new System.ServiceProcess.ServiceBase[] { new
AutoInterpreterService() };

System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}

protected override void OnStart(string[] args)
{
_keepProcessing = true;

_loopThread = new Thread(new ThreadStart(AutomatedProcessingMain));
_loopThread.IsBackground = true;
_loopThread.Start();
}

protected override void OnStop()
{
_keepProcessing = false;

while(_loopThread.IsAlive)
{
LogMessage("Waiting . . . " +
System.DateTime.Now.ToLongTimeString());
System.Threading.Thread.Sleep(10000);
}
}
 
A

Alan Pretre

Matt C. said:
1) Is my general approach below sensible, or is there a better way to
achieve the same basic effect?

I would use a timer, then do your processing in the timer event handler. If
you would be accessing the timer from multiple threads then you would use
the System.Threading.Timer class, otherwise use the System.Timers.Timer
class. Note that the timer click runs on a threadpool thread, so it would
not necessarily be required to create a new thread yourself.

Pseudocode:

void OnStart() {
Timer.Interval = 15 seconds;
Timer.Start();
_Stop = false;
}

void OnStop() {
Timer.Stop();
_Stop = true;
}

void Timer_Click() {
Timer.Stop();

Do processing here.
You can check the value of _Stop at key places and
exit if this processing is time consuming.

if (!_Stop) Timer.Start(); // Do it this way in case OnStop has been
called while processing.
}
2) Is there some way to make OnStop wait until the loop is really done
rather than just giving up waiting and killing the process?

I don't think this is a problem with the way I showed you. It is not
necessary to wait for it to stop, since once the timer event handler exits
it will not be reentered again.

-- Alan
 
M

Matt C.

I would use a timer, then do your processing in the timer event
handler. If you would be accessing the timer from multiple threads
then you would use the System.Threading.Timer class, otherwise use the
System.Timers.Timer class. Note that the timer click runs on a
threadpool thread, so it would not necessarily be required to create a
new thread yourself.

Thanks for the response and the example.

OnStart, OnStop snipped.
void Timer_Click() {
Timer.Stop();

Do processing here.
You can check the value of _Stop at key places and
exit if this processing is time consuming.

if (!_Stop) Timer.Start(); // Do it this way in case OnStop has
been
called while processing.
}


I don't think this is a problem with the way I showed you. It is not
necessary to wait for it to stop, since once the timer event handler
exits it will not be reentered again.

However, if Timer_Click takes several minutes to complete, and OnStop is
called in the middle, it's going to kill Timer_Click() just the same way it
kills my AutomatedProcessingMain(), right?

What my application is doing is taking text files and processing them to a
database. A file might take several minutes to finish. I don't THINK a
forcible terminate in the middle of a file will cause any major problems,
only a leave little mess behind. I'd just rather avoid it if possible.

Peppering the file processing code with checks to _Stop is an idea, however
I am using that code in a desktop, non-service version of the app as well.
I think I'm not going to go there now, though I'll remember the suggestion.

I do think I will switch to the Timer idiom, probably easier for my
hypothetical successor to understand.

It occured to me that if there was just a way to abort OnStop entirely, I
could make that work. It sure does not look like it, though. If I could
set CanStop to false after the service was already running that would be an
acceptable workaround . . .

Anyway, thanks again for the suggestions.

Matt
 
A

Alan Pretre

Hi.

Matt C. said:
However, if Timer_Click takes several minutes to complete, and OnStop is
called in the middle, it's going to kill Timer_Click() just the same way it
kills my AutomatedProcessingMain(), right?

The last line of Timer_Click checks for the case where OnStop was called
during processing. It will not re-enable the timer when finished in that
case.
What my application is doing is taking text files and processing them to a
database. A file might take several minutes to finish. I don't THINK a
forcible terminate in the middle of a file will cause any major problems,
only a leave little mess behind. I'd just rather avoid it if possible.

Peppering the file processing code with checks to _Stop is an idea, however
I am using that code in a desktop, non-service version of the app as well.
I think I'm not going to go there now, though I'll remember the
suggestion.

Basically you have one thread (OnStop) that needs to communicate to another
thread (Timer) that it is time to stop. Since you say the processing task
can take several minutes to complete, what I would do is, create a database
transaction, then somewhere in your processing loop(s) (eg, reading the next
line from a file, or opening the next file, etc.) check the _Stop flag. If
that signals to stop, then rollback the database transaction and exit as
soon as you can. Using *one* strategic placement of the _Stop check in the
innermost loop nesting is probably sufficient to respond very nicely to the
OnStop command.
It occured to me that if there was just a way to abort OnStop entirely, I
could make that work. It sure does not look like it, though. If I could
set CanStop to false after the service was already running that would be an
acceptable workaround . . .

I don't like that idea - the service should be under the control of people,
not the other way around. You want your service as responsive as possible
to the users.

-- Alan
 

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