In one of my projects, I need to schedule a task to be done after some specified interval in the background thread. I use the ThreadPool.RegisterWaitForSingleObject
mechanism to do that.
According to the documentation, the first argument could be a WaitHandle
which has many sub classes. At first I preferred ManualResetEvent
; I use this event to signal to fulfill the schedule and exit. Here is my code:
public class ScheduleTester
{
private long m_LastTicks;
private RegisteredWaitHandle m_RegisterWaitHandle;
public ManualResetEvent Event;
public ScheduleTester()
{
Event = new ManualResetEvent(false);
m_RegisterWaitHandle = ThreadPool.RegisterWaitForSingleObject(Event, new WaitOrTimerCallback(WaitOrTimerCallback), null, 500, false);
m_LastTicks = DateTime.Now.Ticks;
}
private void WaitOrTimerCallback(object state, bool timedOut)
{
long ticks = DateTime.Now.Ticks;
if (timedOut)
{
Console.WriteLine(string.Format("Timeout : {0} ... Do scheduled job takes 1 seconds long, Thread ID : {1}", (ticks - m_LastTicks) / 10000f, Thread.CurrentThread.GetHashCode()));
}
else
{
Console.WriteLine(string.Format("Signaled....Unregister , Thread ID : {0}", Thread.CurrentThread.GetHashCode()));
if (null != m_RegisterWaitHandle)
m_RegisterWaitHandle.Unregister(null);
}
m_LastTicks = ticks;
}
}
static void Main()
{
ScheduleTester waitFor = new ScheduleTester();
Console.ReadKey();
waitFor.Event.Set();
Console.ReadKey();
}
What I expected is to see many lines which notify a scheduled job has done until I press a key to signal the event and a single line noticed the scheduled fulfilled. But what I see is:
When the ManualResetEvent
is replaced with AutoResetEvent
every thing is good.
Yes, that is not exactly very intuitive behavior. You can easily see the underlying problem from the debugger's Debug > Windows > Threads window. You'll see a large number of tp threads all executing your WaitOrTimerCallback() method.
It is up to you to stop this from happening. The executeOnlyOnce
parameter of RWFSO is very, very important. You now pass false
, that makes it important that the sync object you wait for gets signaled only once. Like AutoResetEvent does.
If you use a ManualResetEvent then you must pass true. And tweak your callback to:
private void WaitOrTimerCallback(object state, bool timedOut) {
long ticks = DateTime.Now.Ticks;
if (timedOut) {
Console.WriteLine(string.Format("Timeout : {0} ... Do scheduled job takes 1 seconds long, Thread ID : {1}", (ticks - m_LastTicks) / 10000f, Thread.CurrentThread.ManagedThreadId));
ThreadPool.RegisterWaitForSingleObject(Event, new WaitOrTimerCallback(WaitOrTimerCallback), null, 500, true);
}
else {
Console.WriteLine(string.Format("Signaled....Unregister , Thread ID : {0}", Thread.CurrentThread.ManagedThreadId));
m_RegisterWaitHandle.Unregister(null);
}
m_LastTicks = ticks;
}
You'll now see it detecting timeouts at the wait interval. Not sure if that was intended.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With