C# supports what I call waitForEvent threads or formally known as synchronous event threads. This is a thread that is fired whenever an event in another thread occurs. I use this type of thread to perform an action when a bit transitions from true to false, a variable transitions above/below a limit. The reason for doing this is because we do not want to interrupt the timer thread which checks these values on a 1-sec basis with the chore of building & sending messages, updating database tables, etc.
The design of L1 Monitor is to allow the timer thread to strictly check any transitions in bits or integers . Initially, the threads are created and then wait until its event is set from another thread.
There are two types of synchronous event thread: ManualResetEvent and AutoResetEvent. I make use of the AutoResetEvent since the timer thread cannot waitupon a worker thread from completing its task.
Later, you will see how the worker thread is not fired by the timer when it is still running.
The worker thread has two states: signaled & reset. Initially, the worker thread enters the reset state. It wait for the event by making a call to WaitOne(). A signal state is set by the calling thread when issuing a call to Set(). When the thread is initiated, it waits for the event . When the signal state is Set, the call to WaitOne() returns and the worker thread resumes execution. Since I am using the AutoResetEvent worker thread, it automatically enters a reset state upon event notification.
Since parameters are not passed into the worker thread (because it waiting for the event), we make use of static volatile variables to pass variables between the calling thread and the worker thread. Static makes the variable global while volatile indicates that a field might be modified by multiple threads.
Here is an example on using a worker thread (comments explain what is happening…)
First, define the synchronous events using EventWaitHandle. Initialize the event as non-signaled or reset state.
/* Define the sync event. Specify the constructor with false to
indicate the thread will be created whose state is initialized
as reset */
static EventWaitHandle Level1Tbl = new AutoResetEvent(false);
Define the variables that are shared with the worker threads:
static volatile int iActivePOR;
static volatile int iHeadCropFtg;
/* This flag is set when the worker thread is running.
The calling thread checks this flag before signaling the
event to make sure that the worker event is not signaled
when it is already running */
static volatile bool boLevel1TblWorking = false;
In the service’s OnStart event, define and start the worker thread. Since the thread is created whose state is reset, the threads wait until needed.
/* Create worker thread and start it */
new Thread(HeadCropWorker).Start();
Create the worker thread as a function in the same class as the calling thread:
static void HeadCropWorker()
{
StringBuilder sb = new StringBuilder();
evtCropFtgThrd.WriteEntry("Head crop thread starting...",
EventLogEntryType.Information);
while (true)
{
string sActiveCoilNbr;
/* Wait here until the event is signaled */
HeadCropEvt.WaitOne();
/* Set the flag to indicate that the thread is running */
boHeadCropWorking = true;
if (iActivePOR == 1)
sActiveCoilNbr = sCoilNbrAtPOR1;
else
sActiveCoilNbr = sCoilNbrAtPOR2;
sb.Length = 0;
sb.Append("Head crop received for coil ").Append(sActiveCoilNbr);
sb.Append(" Active POR: ").Append(iActivePOR.ToString());
sb.Append(" head crop ftg: ").Append(iHeadCropFtg.ToString());
evtCropFtgThrd.WriteEntry(sb.ToString(), EventLogEntryType.Information);
/* Thread is finished; ready for next event signal */
boHeadCropWorking = false;
}
}
The calling thread calls the worker thread:
iActivePOR = (arByteEntry[clsGlobalDefs.POR1_ACTIVE_ON] == 1 ? 1 : 2);
iHeadCropFtg = arIntEntry[clsGlobalDefs.ENTRY_HD_CROP_FTG];
if (!boHeadCropWorking)
{
/* Set the event state to fire off the worker thread */
HeadCropEvt.Set();
}
else
evt.WriteEntry("Head Crop Ftg worker still running...", EventLogEntryType.Warning);