簡體   English   中英

如果方法A的調用時間不超過N秒,則調用方法B

[英]Call Method B if method A is not called for more than N seconds

我正在使用以下代碼在N秒后調用方法A來調用方法B。 如果在N秒的超時時間內再次調用方法A,我必須將計時重新設置為N秒。

我無法在項目中引用System.Windows.Form,因此無法使用System.Windows.Form.Timer。

方法B必須在調用線程A的同一線程中調用。

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

}

實際上,代碼可以工作,但是我認為這有點令人困惑。 您是否知道有最佳實踐來達到相同的結果?

停留在.NET 2.0上真可惜,因為Rx擴展具有Throttle方法,可以非常優雅地實現此效果。

遺憾的是,Rx至少需要.NET 3.5 SP1。

那好吧! 您始終可以使用System.Threading.Timer來完成此操作。 可以通過利用當前的SynchronizationContext (這是BackgroundWorker所做的事情)來提供SynchronizationContext

這是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 Forms應用程序來演示此類。 該表單包含一個LaggedMethodPair成員,超時為2000 ms。 它的primaryAction將項目添加到列表視圖。 它的laggedCallback將突出顯示的項目添加到列表視圖。

您可以看到代碼按預期運行。

LaggedMethodPair測試表

我會將這種功能封裝到帶有其他類可以訂閱的事件(例如timer.tick事件)的計時器類中。

我正在嘗試使用AutoResetEvent ,因為它能夠等待信號。 我用它來讓worker等待來自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()中進行同步。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM