【问题标题】:Telling the timer object to invoke its "Elapsed" event asynchronously告诉计时器对象异步调用其“Elapsed”事件
【发布时间】:2010-08-30 07:32:45
【问题描述】:

在我的应用程序中,有时我需要手动调用计时器。

我尝试了以下方法:

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

但这并不一致。

我创建了一个新计时器,继承自 System.Timers.Timer,并公开了一个“Tick”方法 - 但问题是“Elapsed”事件随后同步触发。

当我使用新线程实现“Tick”时 - 结果再次不一致。

有没有更好的实现方式?

【问题讨论】:

  • 在哪些方面结果不一致?
  • 1.由于使用了 Thread.Start,而不是 ThreadPool.Queue(我不想使用...太丑了),线程被关闭 2. 当用户多次调用“Tick”时(在方法停止之前) -我希望它每次都等待第一次调用完成,然后才调用它

标签: c# asynchronous timer


【解决方案1】:

我曾经遇到过同样的问题,所以我用 AutoResetEvent 来知道 Elapsed 是否被成功调用:

/// <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);
    }
}

【讨论】:

  • 我正在使用“Moke”,因为它的构造函数是私有的
  • 哪个命名空间包含 Mock?
  • 这是我制作的自定义助手类。请改用以下内容:System.Timers.ElapsedEventArgs ea = (ElapsedEventArgs)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(ElapsedEventArgs)); typeof(ElapsedEventArgs).GetField("signalTime", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(ea, DateTime.Now); TickableTimer_Elapsed(this,ea);
【解决方案2】:

感觉你应该看看你的设计。通常我会尽量避免让事件处理程序方法包含正在完成的实际工作,但我宁愿让它只是一个触发器,调用其他一些执行工作的方法。这样你也可以从其他任何地方调用其他方法:

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

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

现在,您可以从类中的任何其他位置调用MethodThatDoesTheWork(同步或使用单独的线程异步)。

或者,如果MethodThatDoesTheWork 应该始终是一个异步调用,您可以改为在该方法中生成线程:

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

在这些示例中,我手动创建了线程。您可以使用该方法,ThreadPoolTask 或任何其他异步调用代码的方法,以最适合您的上下文为准。

【讨论】:

  • 正如我的问题所暗示的 - 使用 Thread.Start 并不能解决问题
  • @Fliskov:您的问题表明在单独的线程上调用 Tick 事件并不能解决问题。我的建议是完全避免调用它。也许您可以详细说明您的代码在问题中的实际作用(这样更容易提供有用的答案)?
【解决方案3】:

通常您不需要手动触发计时器 - 您始终可以在新线程中运行事件本身。总的来说,这基本上就是计时器的作用,而且由于您想手动触发它,所以不需要计时器(用于手动调用)。

您没有详细说明“不一致”的含义。以下应该正常工作:

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

当然,myDelegate 可以是 lambda,以防您需要传递参数:

Thread thread = new Thread(() => myMethod(param1, param2));
thread.Start();

【讨论】:

  • 我试过了 - 但结果总是不一致......尤其是当用户多次调用“Tick”时
  • @Fliskov:现在说“它不一致”并不比在你的问题中有用。有详细的吗?
  • 看@Fredreck的评论
猜你喜欢
  • 1970-01-01
  • 2014-09-25
  • 2015-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多