views:

41

answers:

3

There are times in my application, when I need to invoke my timer manually.

I've tried the following:

int originalInterval = t.Interval;
t.Interval = 0;
t.Interval = originalInterval;

but it wasn't consistent.

I've created a new timer, inheriting from System.Timers.Timer, and exposed a "Tick" method - but the problem was that the "Elapsed" event then fired synchronously.

When I implemented the "Tick" with a new Thread - the results were, again, not consistent.

Is there a better way to implement it?

+1  A: 

I once had the same problem, so I used the AutoResetEvent to know if the Elapsed was invoked successfully:

/// <summary>
/// Tickable timer, allows you to manually raise a 'Tick' (asynchronously, of course)
/// </summary>
public class TickableTimer : System.Timers.Timer
{
    public new event ElapsedEventHandler Elapsed;

    private System.Threading.AutoResetEvent m_autoResetEvent = new System.Threading.AutoResetEvent(true);

    public TickableTimer()
        : this(100)
    {
    }

    public TickableTimer(double interval)
        : base(interval)
    {
        base.Elapsed += new ElapsedEventHandler(TickableTimer_Elapsed);
    }

    public void Tick()
    {
        new System.Threading.Thread(delegate(object sender)
        {
            Dictionary<string, object> args = new Dictionary<string, object>
            {
                {"signalTime", DateTime.Now},
            };
            TickableTimer_Elapsed(this, Mock.Create<ElapsedEventArgs>(args));
        }).Start();
        this.m_autoResetEvent.WaitOne();
    }

    void TickableTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        m_autoResetEvent.Set();
        if (this.Elapsed != null)
            this.Elapsed(sender, e);
    }
}
Nissim
Great dude!!! Nicely done! (using the 'Mock' is brilliant!)
Fliskov
I'm using 'Moke' because it's constructor is private
Nissim
A: 

Normally you shouldn’t need to fire a timer manually — you can always just run the event itself in a new thread. By and large, that’s basically what the timer does, and since you want to fire it manually, you don’t need the timer (for that manual invocation).

You didn’t specify any details as to what you mean by “not consistent”. The following should normally work:

Thread thread = new Thread(myDelegate);
thread.Start();

Of course, myDelegate can be a lambda in case you need to pass parameters:

Thread thread = new Thread(() => myMethod(param1, param2));
thread.Start();
Timwi
I tried that - but the results were always inconsistent... especially when the user called "Tick" many times
Fliskov
@Fliskov: Saying “it is inconsistent” is no more useful now than it was in your question. Any detail?
Timwi
@Timwi: Look at the comment to @Fredreck
Nissim
+1  A: 

It feels like you should look at your design a bit. Typically I try to avoid having the event handler method contain the actual work being done, but I rather try to let it be just a trigger, calling some other method that performs the work. That way you can invoke that other method from anywhere else as well:

private void Timer_Tick(object sender, EventArgs e)
{
    new Thread(MethodThatDoesTheWork).Start();
}

private void MethodThatDoesTheWork()
{
    // actual work goes here
}

Now, you can invoke MethodThatDoesTheWork from anywhere else within the class (either synchronously or asynchronously using a separate thread).

Alternatively, if MethodThatDoesTheWork should always be an asynchronous call, you can spawn the thread inside that method instead:

private void MethodThatDoesTheWork()
{
    new Thread(() =>
    {
         // work code goes here
    }).Start();
}

In these samples I have manually created threads. You can use that approach, the ThreadPool, Task or whatever other method of calling code asychronously, whichever fits best in your context.

Fredrik Mörk
As my question implied - using Thread.Start doesn't solve the problem
Fliskov
@Fliskov: your question states that invoking the `Tick` event on a separate thread does not solve the problem. My suggestion is to avoid invoking it at all. Perhaps you can elaborate a bit on what your code actually does in the question (that would make it easier to provide a useful answer)?
Fredrik Mörk