如果方法A的调用时间超过N秒,则调用方法B.

时间:2010-09-10 11:49:11

标签: c# .net multithreading c#-3.0

在调用N秒方法A后,我使用以下代码调用方法B.如果方法A 在N秒超时内再次调用,我必须重置计数回N秒的时间。

我无法在我的项目中引用System.Windows.Form,因此我无法使用System.Windows.Form.Timer。

必须在同一个线程A中调用方法B。

private void InitTimer()
{
    timer = new BackgroundWorker();
    timer.WorkerSupportsCancellation = true;
    timer.WorkerReportsProgress = true;
    timer.DoWork += delegate(object sender, DoWorkEventArgs e)
                    {
                        var st = DateTime.Now;
                        while (DateTime.Now.Subtract(st).TotalSeconds < 10)
                        {
                            if (timer.CancellationPending)
                            {
                                e.Cancel = true;
                                return;
                            }
                        }

                    };
    timer.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
                    {
                        if (!e.Cancelled)
                        {    
                            MethodB();
                        }
                        else
                        {
                            timer.RunWorkerAsync();
                        }
                    };
}



public void MethodA()
{
     if (timer.IsBusy)
         timer.CancelAsync();
     else
         timer.RunWorkerAsync();

}

public void MethodB()
{
     //do some stuff

}

实际上代码工作,但我认为这有点令人困惑。你知道是否有最佳实践来达到同样的效果吗?

3 个答案:

答案 0 :(得分:4)

遗憾的是你被困在.NET 2.0上,因为Rx extensionsThrottle方法可以非常优雅地实现这种效果。

可悲的是,Rx至少需要.NET 3.5 SP1。

哦,好吧!您可以随时使用System.Threading.Timer来完成此操作。可以通过利用当前SynchronizationContext来提供同步(这是BackgroundWorker所做的。)

这是一个LaggedMethodPair类的草图来说明这种方法。该类在其构造函数中接受三个输入:一个Action按需执行,另一个Action作为将在给定超时时间后调用的回调,当然,超时本身:

public sealed class LaggedMethodPair
{
    private SynchronizationContext _context;
    private Timer _timer;

    private Action _primaryAction;
    private Action _laggedCallback;
    private int _millisecondsLag;

    public LaggedMethodPair(Action primaryAction,
                            Action laggedCallback,
                            int millisecondsLag)
    {
        if (millisecondsLag < 0)
        {
            throw new ArgumentOutOfRangeException("Lag cannot be negative.");
        }

        // Do nothing by default.
        _primaryAction = primaryAction ?? new Action(() => { });

        // Do nothing by default.
        _laggedCallback = laggedCallback ?? new Action(() => { });

        _millisecondsLag = millisecondsLag;

        _timer = new Timer(state => RunTimer());
    }

    public void Invoke()
    {
        // Technically there is a race condition here.
        // It could be addressed, but in practice it will
        // generally not matter as long as Invoke is always
        // being called from the same SynchronizationContext.
        if (SynchronizationContext.Current == null)
        {
            SynchronizationContext.SetSynchronizationContext(
                new SynchronizationContext()
            );
        }

        _context = SynchronizationContext.Current;

        ResetTimer();

        _primaryAction();
    }

    void ResetTimer()
    {
        _timer.Change(_millisecondsLag, Timeout.Infinite);
    }

    void RunTimer()
    {
        _context.Post(state => _laggedCallback(), null);
    }
}

我编写了一个示例Windows窗体应用程序来显示此类的实际操作。表单包含LaggedMethodPair成员,超时为2000毫秒。其primaryAction将一个项添加到列表视图中。其laggedCallback将突出显示的项添加到列表视图中。

您可以看到代码按预期运行。

LaggedMethodPair Test Form

答案 1 :(得分:1)

我会将此功能封装到一个timer类中,其中包含其他类可以订阅的事件(例如timer.tick事件)。

答案 2 :(得分:0)

我正在尝试使用AutoResetEvent,因为它能够等待信号。我用它让工人等待来自A()的信号,如果它已经太长,将调用B()。

class Caller
{
    AutoResetEvent ev = new AutoResetEvent(false);
    public void A()
    {
        ev.Set();
        // do your stuff
        Console.Out.WriteLine("A---");
    }
    void B()
    {
        Console.Out.WriteLine("B---");
    }

    public void Start()
    {
        var checker = new BackgroundWorker();
        checker.DoWork += new DoWorkEventHandler(checker_DoWork);
        checker.RunWorkerAsync();
    }

    void checker_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;
        while (!worker.CancellationPending)
        {
            bool called = ev.WaitOne(TimeSpan.FromSeconds(3));
            if (!called) B();
        }
    }
}

我已经粗略地测试了我的课程,到目前为止工作正常。请注意,将从工作线程调用B,因此如果需要,您必须在B()中进行同步。