How do I make System.Timer callback on a separate threadpool, or on a dedicated thread?
Background
I have a lightweight monitor for a critical application using a System.Timer and can't figure out how to make the callback not be in a ThreadPool, and to operate at a high priority.
The reasons for this are:
If the critical app is non-responsive, the ThreadPool will send a cancellation token, or Thread.Abort, or Win32 Abort (that doesn't run a finalizer) to the hanging thread.
If the app is CPU bound, then my monitoring thread should run at a Highest priority to prevent a denial of service to my monitor
The threadpool may be exhausted due to too many threads, or a value of MaxThreads that is too low
it's not possible to set a priority on a Thread that is in the threadpool (AFAIK)
As a result I think its necessary to have a Timer and it's callback on a separate thread, though I don't know how to achieve this.
If this is possible by creating a separate AppDomain, will the CLR still allow me to issue commands to other threads in a different AppDomain?
I suppose you could do this by using Thread.Sleep
on a dedicated thread. That is:
void MonitorProc(object state)
{
while (!ShutdownSignaled)
{
Thread.Sleep(MonitorFrequency);
// Do monitor stuff here
}
}
If you set that thread at high priority, it's likely that it'll work as desired. It's not a great solution, though.
However, you still have the problem that one of your threads could crash, which would take down the entire application--including the monitor.
You might consider making the monitor a separate application that runs at high priority and sends commands to the main application using sockets or WCF or some other communications channel. That way, the monitor won't die if the main application crashes. You could code the main application to start the monitor automatically at startup if it isn't already there.
I wonder, though, just how likely it is that the conditions you list will actually happen. Let's go down the list.
I don't understand your first point here, unless it's to say that the program could crash. In that case, then you should just write a separate monitor application.
If the app is CPU bound, your monitor thread will still get a timeslice if all the threads are at the same priority. Even if the other threads are at a higher priority, your monitor will still get a timeslice now and then. Unless the threads are at RealTime priority. And you don't want to do that.
If you run out of threads, then there's a fundamental problem with your program--a problem that you probably should have found before you put it in production.
The most reliable way to do this monitoring would be with a separate application.
I'm not aware of a way to do this using a System.Timer
. However you can resort to a System.Windows.Forms.Timer
in order to have a timer that runs in a specific thread that you create this way allowing you to set its priority explicitly.
This works because the Tick
event of a System.Windows.Forms.Timer
is always triggered in the thread that created it, normally the UI thread, but you can have a thread without UI running a message loop and achieve the same.
See if the following is acceptable:
static void Main(string[] args)
{
var monitorContext = new System.Windows.Forms.ApplicationContext();
var monitor = new Thread(Monitor) { Priority = ThreadPriority.Highest };
monitor.Start(monitorContext); // Start monitor
Thread.Sleep(5500); // ...
monitorContext.ExitThread(); // Terminates monitor
}
public static void Monitor(object context)
{
var timer = new System.Windows.Forms.Timer()
{
Enabled = true,
Interval = 1000
};
timer.Tick += new EventHandler(Monitor_Tick);
System.Windows.Forms.Application.Run(
(System.Windows.Forms.ApplicationContext)context);
}
private static void Monitor_Tick(object sender, EventArgs e)
{
Debug.WriteLine(
"{0}|{1}",
DateTime.UtcNow.ToString(),
Thread.CurrentThread.Priority);
}
The only thing you need to consider is that if a tick event is fired while a previous one is still being processed then it will be discarded, so make sure what you do in the tick event does not take longer than the interval configured for the timer.