views:

66

answers:

2

I have a System.Timers.Timer object that I want to use, but I don't want the processing tied to the Timer to interfere with normal to high priority threads. In other words, I'd like to say that I want to process X every 5 seconds as long as nothing else is running.

How could I ensure that my Timer operations are running in a low-priority manner?

A: 

Probably the easiest way would be to have a "busy" flag (or count), and ignore the timer ticks as long as it's non-zero.

P.S. Changing thread priorities is not recommended. It's hardly ever necessary.

Stephen Cleary
What I'm trying to do is make sure my application doesn't affect other applications on the server. I'm writing a profiling application.
Michael Hedgpeth
Changing thread priorities of other applications can be problamatic, but it's hardly ever harmful to lower the priority of your own application. @Michael Hedgpeth - But a profiling application is possibly a counter-example!
Jeffrey L Whitledge
@Jeffrey: I disagree. The correct prioritization of threads is counter-intuitive, and with the dynamic priority boosting built-in to the Windows scheduler, manual adjusting of thread priority is often counterproductive.
Stephen Cleary
@Stephen Cleary - Yes, boosing priority manually can be counterproductive, but if you have a process that you want to run in the background when nothing else is running, and you know that higher priority processes are not going to share any resources (once the Loader Lock is released, of course), then lowering a priority isn't likely to hurt anything. Unless I'm really missing something, which is certainly possible. What am I missing?
Jeffrey L Whitledge
+1  A: 

The nice thing about System.Timers.Timer is that you can assign a synchronzing object via the SynchronizingObject property and then exploit it to run the Elapsed event a thread whose priority can be controlled.

Just assign an instance of the ElapsedEventReceiver to the SynchronizingObject property of your timer.

Disclaimer: I whipped this up pretty fast so you will need to add your own finishing touches to make it more robust.

public class ElapsedEventReceiver : ISynchronizeInvoke
{
    private Thread m_Thread;
    private BlockingCollection<Message> m_Queue = new BlockingCollection<Message>();

    public ElapsedEventReceiver()
    {
        m_Thread = new Thread(Run);
        m_Thread.Priority = ThreadPriority.BelowNormal;
        m_Thread.IsBackground = true;
        m_Thread.Start();
    }

    private void Run()
    {
        while (true)
        {
            Message message = m_Queue.Take();
            message.Return = message.Method.DynamicInvoke(message.Args);
            message.Finished.Set();
        }
    }

    public IAsyncResult BeginInvoke(Delegate method, object[] args)
    {
        Message message = new Message();
        message.Method = method;
        message.Args = args;
        m_Queue.Add(message);
        return message;
    }

    public object EndInvoke(IAsyncResult result)
    {
        Message message = result as Message;
        if (message != null)
        {
            message.Finished.WaitOne();
            return message.Return;
        }
        throw new ArgumentException("result");
    }

    public object Invoke(Delegate method, object[] args)
    {
        Message message = new Message();
        message.Method = method;
        message.Args = args;
        m_Queue.Add(message);
        message.Finished.WaitOne();
        return message.Return;
    }

    public bool InvokeRequired
    {
        get { return Thread.CurrentThread != m_Thread; }
    }

    private class Message : IAsyncResult
    {
        public Delegate Method;
        public object[] Args;
        public object Return;
        public object State;
        public ManualResetEvent Finished = new ManualResetEvent(false);

        public object AsyncState
        {
            get { return State; }
        }

        public WaitHandle AsyncWaitHandle
        {
            get { return Finished; }
        }

        public bool CompletedSynchronously
        {
            get { return false; }
        }

        public bool IsCompleted
        {
            get { return Finished.WaitOne(0); }
        }
    }
}
Brian Gideon
One important piece of information that this answer assumes without addressing is the fact that on Windows a lower priority thread will not be scheduled to run so long as higher priority threads are available to run (usually). This means that setting the thread's priorty will achieve what you want. This fact may not be obvious, since some may assume that priorty only adjusts the size of the timeslices or some other scheme. Note that Windows will avoid "starving" a low-priority thread if it hasn't run in a while, but it doesn't sound like that's really an issue.
Jeffrey L Whitledge
@Jeffrey: Good point. In fact that actually could be problematic in my current example because the marshalled messages for execution would start stacking up possibly overflowing the queue. I will work on modifying this when I get a chance.
Brian Gideon