Лучший способ создать функцию с однократным запуском в C # - PullRequest
41 голосов
/ 06 мая 2011

Я пытаюсь создать функцию, которая принимает действие и тайм-аут и выполняет действие после тайм-аута. Функция должна быть неблокирующей. Функция должна быть поточно-ориентированной. Я также очень, очень хочу избежать Thread.Sleep ().

Пока лучшее, что я могу сделать, это:

long currentKey = 0;
ConcurrentDictionary<long, Timer> timers = new ConcurrentDictionary<long, Timer>();

protected void Execute(Action action, int timeout_ms)
{
    long currentKey = Interlocked.Increment(ref currentKey);
    Timer t = new Timer(
      (key) =>
         {
           action();
           Timer lTimer;
           if(timers.TryRemove((long)key, out lTimer))
           {
               lTimer.Dispose();
           }
         }, currentKey, Timeout.Infinite, Timeout.Infinite
      );

     timers[currentKey] = t;
     t.Change(timeout_ms, Timeout.Infinite);
}

Проблема в том, что вызов Dispose () из самого обратного вызова не может быть хорошим. Я не уверен, безопасно ли «падать» с конца, то есть таймеры считаются живыми, пока выполняются их лямбды, но даже если это так, я бы предпочел правильно распоряжаться им.

«Один выстрел с задержкой» кажется такой распространенной проблемой, что должен быть простой способ сделать это, возможно, какая-то другая библиотека в System.Threading, которую я пропускаю, но сейчас единственное решение, которое я могу придумать является модификацией вышеупомянутого с выделенной задачей очистки, выполняющейся с интервалом. Любой совет?

Ответы [ 12 ]

0 голосов
/ 24 июля 2015

Это может быть немного поздно, но вот решение, которое я сейчас использую для обработки отложенного выполнения:

public class OneShotTimer
{

    private volatile readonly Action _callback;
    private OneShotTimer(Action callback, long msTime)
    {
        _callback = callback;
        var timer = new Threading.Timer(TimerProc);
        timer.Change(msTime, Threading.Timeout.Infinite);
    }

    private void TimerProc(object state)
    {
        try {
            // The state object is the Timer object. 
            ((Threading.Timer)state).Dispose();
            _callback.Invoke();
        } catch (Exception ex) {
            // Handle unhandled exceptions
        }
    }

    public static OneShotTimer Start(Action callback, TimeSpan time)
    {
        return new OneShotTimer(callback, Convert.ToInt64(time.TotalMilliseconds));
    }
    public static OneShotTimer Start(Action callback, long msTime)
    {
        return new OneShotTimer(callback, msTime);
    }
}

Вы можете использовать его так:

OneShotTimer.Start(() => DoStuff(), TimeSpan.FromSeconds(1))
0 голосов
/ 06 мая 2011

Почему бы просто не вызвать сам параметр действия в асинхронном действии?

Action timeoutMethod = () =>
  {
       Thread.Sleep(timeout_ms);
       action();
  };

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