I am trying to create a function that takes in an Action and a Timeout, and executes the Action after the Timeout. The function is to be non-blocking. The function must be
I don't know which version of C# you are using. But I think you could accomplish this by using the Task library. It would then look something like that.
public class PauseAndExecuter
{
public async Task Execute(Action action, int timeoutInMilliseconds)
{
await Task.Delay(timeoutInMilliseconds);
action();
}
}
Use Microsoft's Reactive Framework (NuGet "System.Reactive") and then you can do this:
protected void Execute(Action action, int timeout_ms)
{
Scheduler.Default.Schedule(TimeSpan.FromMilliseconds(timeout_ms), action);
}
There is nothing built-in to .Net 4 to do this nicely. Thread.Sleep or even AutoResetEvent.WaitOne(timeout) are not good - they will tie up thread pool resources, I have been burned trying this!
The lightest weight solution is to use a timer - particularly if you will have many tasks to throw at it.
First make a simple scheduled task class:
class ScheduledTask
{
internal readonly Action Action;
internal System.Timers.Timer Timer;
internal EventHandler TaskComplete;
public ScheduledTask(Action action, int timeoutMs)
{
Action = action;
Timer = new System.Timers.Timer() { Interval = timeoutMs };
Timer.Elapsed += TimerElapsed;
}
private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Timer.Stop();
Timer.Elapsed -= TimerElapsed;
Timer = null;
Action();
TaskComplete(this, EventArgs.Empty);
}
}
Then, create a scheduler class - again, very simple:
class Scheduler
{
private readonly ConcurrentDictionary<Action, ScheduledTask> _scheduledTasks = new ConcurrentDictionary<Action, ScheduledTask>();
public void Execute(Action action, int timeoutMs)
{
var task = new ScheduledTask(action, timeoutMs);
task.TaskComplete += RemoveTask;
_scheduledTasks.TryAdd(action, task);
task.Timer.Start();
}
private void RemoveTask(object sender, EventArgs e)
{
var task = (ScheduledTask) sender;
task.TaskComplete -= RemoveTask;
ScheduledTask deleted;
_scheduledTasks.TryRemove(task.Action, out deleted);
}
}
It can be called as follows - and is very lightweight:
var scheduler = new Scheduler();
scheduler.Execute(() => MessageBox.Show("hi1"), 1000);
scheduler.Execute(() => MessageBox.Show("hi2"), 2000);
scheduler.Execute(() => MessageBox.Show("hi3"), 3000);
scheduler.Execute(() => MessageBox.Show("hi4"), 4000);
My example:
void startTimerOnce()
{
Timer tmrOnce = new Timer();
tmrOnce.Tick += tmrOnce_Tick;
tmrOnce.Interval = 2000;
tmrOnce.Start();
}
void tmrOnce_Tick(object sender, EventArgs e)
{
//...
((Timer)sender).Dispose();
}
treze's code is working just fine. This might help the ones who have to use older .NET versions:
private static volatile List<System.Threading.Timer> _timers = new List<System.Threading.Timer>();
private static object lockobj = new object();
public static void SetTimeout(Action action, int delayInMilliseconds)
{
System.Threading.Timer timer = null;
var cb = new System.Threading.TimerCallback((state) =>
{
lock (lockobj)
_timers.Remove(timer);
timer.Dispose();
action();
});
lock (lockobj)
_timers.Add(timer = new System.Threading.Timer(cb, null, delayInMilliseconds, System.Threading.Timeout.Infinite));
}
I use this method to schedule a task for a specific time:
public void ScheduleExecute(Action action, DateTime ExecutionTime)
{
Task WaitTask = Task.Delay(ExecutionTime.Subtract(DateTime.Now));
WaitTask.ContinueWith(() => action());
WaitTask.Start();
}
It should be noted that this only works for about 24 days out because of int32 max value.