P
Peter Duniho
Good to know.
Interesting. How do I protect Running? If the LoadSchedule only has
one instance of the job class, why wouldn't Running reflect false when
in fact it is set to true?
I assume you mean "why would Running reflect false when in fact it is set
to true".
As for the answer: the issue is that when you start another thread, you
don't really know when the code in that thread will start executing. In
addition, the thread that started the other thread specifically gets to
run until its timeslice runs out or it intentionally yields (by calling
Sleep() with a positive value, for example).
In the code you posted previously, the code that starts jobs simply looks
at the "is running" state of the job instance and starts a new thread for
the job if it's not running. Since it also just sits and loops checking
the jobs, then if it gets back to checking a job before that job's thread
gets a chance to start running and set its own "is running" state, the
loop that starts jobs will try to start the same job twice. There's
nothing in that code that guarantees that the main thread won't get back
to checking a specific job until after that specific job's thread gets a
chance to run.
As for how you could protect against that, there are a variety of ways.
As it happens, the code you posted most recently is quite different from
the previous code you posted. In particular, while you didn't post the
job class that goes with the most recent code you posted, it looks
possible that you set the "is running" state in the main thread rather
than the job's thread. Specifically, if the job's "StartJob()" method
sets this state, then that state gets set in the main thread rather than
the job's thread.
And doing this is in fact one way to ensure that the state is set properly
without any race condition. Since you didn't post the job class the
second time around, I can't say for sure that you've avoided it in that
code. But you might have.
By the way, just some background. I am an old dog trying to learn new
tricks. I am the resident VB6 and ASP guru. Maybe that will help in
translation.
Probably not, since I don't use VB enough for it to be any sort of "lingua
franca" here, and I don't use ASP at all.
Why do you say that?
As I noted above, for the race condition to actually be a problem, the
main thread needs to be able to get back to the top of its loop and look
at the job again before the job's own thread gets a chance to set its
state. By putting a 100ms sleep into the loop, you accomplish a couple of
things: first, you slow down the thread, given the job's thread more time
to get started; more importantly, you force the main thread to yield to
other threads, ensuring that _some_ thread will get a chance to run before
the main thread continues looking at jobs.
This isn't a perfect solution because there are reasons that the job's
thread might still not get a chance to run before the main thread wakes up
again and starts looking at jobs. You definitely wouldn't want to rely on
it to protect against the race condition. But it does reduce the risk
(which IMHO is actually a bad thing...the less often a bug actually
manifests itself, the harder it is to fix. The last thing you want to do
is make a bug happen less frequently, without preventing it altogether).
[...]If the code is in fact pretty much identical to the real-world code
you're Â
using, then you do have a number of other problems that need fixing as
well.
I am interested. What needs fixing?
The main issues in your Form-based application is that you never return
from the Activated event handler and that (probably because of that) you
use Control.Refresh() to update controls on the form. But you said that
your real-world code doesn't use Forms, and so this doesn't seem like a
relevant problem.
The other thing I don't like is the way you instantiate the job classes,
but I am guessing that this is either again an artifact of the sample
code, or there's something about the way that your job data is stored in a
database or other serialized data structure that either encourages or
requires this sort of thing. I prefer not to use reflection, but I don't
have any strong arguments against it and I recognize that some people feel
the code is actually easier to maintain if they use it.
Pete