Reputation: 11
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.
Upvotes: 0
Views: 4387
Reputation: 48949
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.
Upvotes: 6
Reputation: 8531
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: Creating a blocking Queue<T> in .NET?.
Upvotes: 0
Reputation: 48949
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();
}
Upvotes: 2
Reputation: 340168
If you want a timed event to execute on a particular thread, you have at least a couple options:
Upvotes: 1
Reputation: 273159
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.
Upvotes: 3