Вызовите метод B, если метод A не вызывается более N секунд - PullRequest
0 голосов
/ 10 сентября 2010

Я использую следующий код для вызова метода B через N секунд.Если метод A вызывается снова в течение N секунд, я должен сбросить отсчет времени до 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

}

На самом деле код работает, но я думаю, что это немного смущает.Знаете ли вы, есть ли лучшие практики для достижения того же результата?

Ответы [ 3 ]

4 голосов
/ 10 сентября 2010

Жаль, что вы застряли в .NET 2.0, потому что Rx extensions имеет метод Throttle, который достигает этого эффекта довольно элегантно.

К сожалению, Rx требует как минимум .NET3.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 Forms, чтобы показать этот класс в действии.Форма содержит элемент LaggedMethodPair с таймаутом 2000 мс.Его primaryAction добавляет элемент в список.Его laggedCallback добавляет выделенный элемент в представление списка.

Вы можете видеть, что код работает должным образом.

LaggedMethodPair Test Form

1 голос
/ 10 сентября 2010

Я бы инкапсулировал эту функциональность в класс таймера с событиями, на которые могут подписаться другие классы (например, событие timer.tick).

0 голосов
/ 10 сентября 2010

Я пытаюсь использовать 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 ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...