views:

163

answers:

5

I want a timer tick/elapsed event inside a new thread. It seems like I cannot use the Windows timer. But if I use the Timers.Timer, it creates worked thread from the threadpool for each elapsed event. Is there a way to make these events to occur in the same thread?

Update:

Thank you everybody for answering this question.

My intention behind the whole thing might sound a little crazy though. I want you to correct me. This is my thinking(as a novice) when I am trying to acieve this. I am trying perform a task every 2 seconds. When I use Timers.Timer, it creates a thread every 2 seconds which I am thinking it as an overhead.

My main thread and my other threads need a lot of processor's time to perform their tasks. So, if I can avoid creating these Threads, I am saving whatever microseconds of the processor to create a thread each time timer elapsed, for my main and other threads.

I made a quick tests and compared few solutions. 1000 msec interval in each case. 100 ticks.

Solution1: infinite loop with waits/sleep {00:01:42.0068344}

solution2: Using Brian's Synchronizer {00:01:42.4068573}

solution3: Timers.Timer as it is {00:01:42.4018571}

This should tell me that 2.0068344, 2.4068573, 2.4018571 are the times wasted for other things in the background other than the time interval of 1000 msec for 100 ticks. This should mean when solution1 meet your needs, it is the best solution performance-wise??

This should also mean that though Brian's solution is synchronized to one thread, it is in-fact creating threads in the background.

Please confirm it or correct me.

+3  A: 

Is there a way to make these events to occur in the same thread?

No, and that would not make much sense either.
An event cannot just 'break in' to a thread, the thread has to cooperate.

What you can do is to create a Thread that loops repeatedly and waits on a semaphore. Then use any kind of Timer to trigger that semaphore.

Henk Holterman
+1, this is the correct answer.
Hans Passant
+4  A: 

Yes. You can make the System.Timers.Timer execute the Elapsed event handler in the same thread. To do this you need to set the SynchronizingObject property to an object that will marshal the execution onto a thread of your choosing. However, it is important to realize that you cannot just inject execution onto a random thread. The receiving thread has to be specifically designed to allow marshaling the execution of method calls.

public static void Main(string[] args)
{
    var timer = new System.Timers.Timer();
    timer.Interval = 1000;
    timer.Elapsed += 
      (s, a) => 
      { 
        Console.WriteLine("{1} {0}", 
          DateTime.Now, Thread.CurrentThread.ManagedThreadId); 
      };
    timer.SynchronizingObject = new Synchronizer();
    timer.Start();
    Console.ReadLine();
}

And here is the code for the Synchronizer class. This code uses the BlockingCollection class from 4.0 of the .NET BCL. If you are not using 4.0 then you can replace it with Stephen Toub's BlockingQueue.

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

    public Synchronizer()
    {
        m_Thread = new Thread(Run);
        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 = null;
        public object[] Args = null;
        public object Return = null;
        public object State = null;
        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); }
        }
    }
}

Update:

You need to understand how System.Timers.Timer works. It actually uses System.Threading.Timer behind the scenes and executes the event handler on a ThreadPool thread. So it is not really creating any threads at all. The same ones from the thread pool are getting recycled over and over again. There is no thread creation overhead. That is the whole point of a thread pool.

Now, the solution I provided here still uses the ThreadPool behind the scenes, but instead of having the work item execute the Elapsed event handler directly it is executing the Synchronizer.BeginInvoke method which marshals the event handler onto the thread that the Synchronizer created.

So it should be no surpise that the solution I provided here would be slower. Personally, I would stick to solution #1 as it is easier. I would not worry a whole lot about performance at this point.

Brian Gideon
Lines of code notwithstanding, I like this solution.
ladenedge
Nice, I edited the writeline to stress the proof. Feel free to rollback
Henk Holterman
@Henk: Good edit.
Brian Gideon
This kind of magic doesn't exist, it can only work if the thread is the UI thread. Only that thread has a synchronization provider, implemented by the message loop. Other threads will use the default provider which will use a threadpool thread. Might as well use System.Windows.Forms.Timer
Hans Passant
@Hans: I am not sure I would classify anything in this answer as magic per se, but whatever it may be called it does work. The `Synchronizer` class is the synchronization provider and creates a thread separate from the UI thread for doing such a thing. I did not think the OP was interested in `System.Windows.Forms.Timer` since it raises the event on the UI thread which was not desired; at least that is the way I read the question. Of course, with all that said, I would not approach this problem using the solution I provided here, but strictly speaking, I do believe it answered the question.
Brian Gideon
I just read the comments attached to the question and the OP confirmed that a solution for getting the `System.Timers.Timer.Elapsed` event to occur on a thread (same one everytime) separate from the UI thread was the intent.
Brian Gideon
Brian, both your solutions work for me. That is what I was looking for.
thanks for the update again.. that clarifies the rest..
+1  A: 

If you want a timed event to execute on a particular thread, you have at least a couple options:

  • spin up the thread you want the 'timer' to run on and simply have it sleep for the timer duration, then do the work you want it to do.
  • have the thread wait on a an event signaled by a timer (as Henk Holterman suggested). This seems like a more complicated way to do the above, but might make sense if you want the thread to be able to be awakened by means other than the timer.
Michael Burr
Henk Holterman
+2  A: 

I thought it would beneficial to provide another answer. The reason is because my other answer complicates things too much. It is really unnecessary to set the SynchronzingObject property on a System.Timers.Timer instance if all you are wanting to do is make sure that a task is executed periodically on the same thread. It is much easier to start a new thread that spins around in an infinite loop executing what you would have originally placed in the Elapsed event.

public static void Main(string[] args)
{
    var stop = new ManualResetEvent(false);
    var thread = new Thread(
        () =>
        {
            while (stop.WaitOne(YOUR_INTERVAL_GOES_HERE))
            {
                // The code that was in the Elapsed event handler goes here instead.
            }
        });
    Console.WriteLine("Press ENTER to stop...");
    Console.ReadLine();
    stop.Set();
    thread.Join();
}
Brian Gideon
A: 

I recommend that you create a thread that watches a blocking queue that is fed by your timer's elapsed event handler. Then it won't matter which thread the event handler uses, and since all it does is drop something into the queue, that thread's duration will be brief.

For a good blocking queue implementation, see Marc Gravell's answer: http://stackoverflow.com/questions/530211/creating-a-blocking-queuet-in-net.

ebpower