PeriodicTimer with dynamic period in a BackgroundService - c#

I'm using
using PeriodicTimer timer = new(_period);
while (!stoppingToken.IsCancellationRequested
&& await timer.WaitForNextTickAsync(stoppingToken))
{
await DoWork();
}
Is there a way to set _period dynamically? that is, take that period of a database for example.

No, a Periodic Timer does not allow period to be changed after instantiation.
You can construct the original or a new different Periodic Timer with a value you got from elsewhere but, be sure the Dispose instances so that the required resources can be released.
If you need a variable delay, your could use Task.Delay instead.

Below is a custom UpdateablePeriodicTimer class, which is essentially a PeriodicTimer with a mutable Period property:
public class UpdateablePeriodicTimer : IDisposable
{
private readonly object _locker = new();
private PeriodicTimer _timer; // Becomes null when disposed
private PeriodicTimer _newTimer;
private TimeSpan _period;
private bool _waiting;
public UpdateablePeriodicTimer(TimeSpan period)
{
_timer = new(period);
_period = period;
}
public TimeSpan Period
{
get { lock (_locker) return _period; }
set
{
PeriodicTimer timerToDispose;
lock (_locker)
{
if (_timer is null) throw new ObjectDisposedException(null,
$"The {nameof(UpdateablePeriodicTimer)} has been disposed.");
if (_waiting)
{
timerToDispose = _newTimer;
_newTimer = new(value);
}
else
{
timerToDispose = _timer;
_timer = new(value);
}
_period = value;
}
timerToDispose?.Dispose();
}
}
public async ValueTask<bool> WaitForNextTickAsync(
CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
ValueTask<bool> waitTask;
lock (_locker)
{
if (_timer is null) return false; // Disposed
if (_waiting) throw new InvalidOperationException();
waitTask = _timer.WaitForNextTickAsync(cancellationToken);
_waiting = true;
}
try { return await waitTask.ConfigureAwait(false); }
finally
{
PeriodicTimer timerToDispose = null;
lock (_locker)
{
_waiting = false;
if (_timer is not null && _newTimer is not null)
{
timerToDispose = _timer;
_timer = _newTimer;
_newTimer = null;
}
}
timerToDispose?.Dispose();
}
}
public void Dispose()
{
PeriodicTimer timerToDispose;
lock (_locker)
{
if (_timer is null) return; // Disposed
timerToDispose = _timer;
_timer = null;
_newTimer?.Dispose();
}
timerToDispose.Dispose();
}
}
Changing the Period starts immediately a new PeriodicTimer, which is swapped with the current PeriodicTimer when the active WaitForNextTickAsync completes, or immediately if there is no active operation.
Online demo.
The UpdateablePeriodicTimer class is thread-safe.
There is an open proposal on the dotnet/runtime repository for adding the Period property in the built-in PeriodicTimer. This proposal is currently in the queue to be reviewed.

Related

Safe to dispose of System.Threading.Timer in timer callback? [duplicate]

I'm using Threading.Timer, like:
new Timer(new TimerCallback(y=>
{
try
{
Save(Read(DateTime.Now));
// here i want to dispose this timer
}
catch
{
}
}),null,100000,10000);
How can I dispose this timer inside of a callback. or workaround?
Update: Let me explain the situation. I want to try to call the method "Save", while it throws an exception. If it works, I need to stop the timer.
Try this:
Timer timer = null;
timer = new Timer(new TimerCallback(y =>
{
try
{
Save(Read(DateTime.Now));
// here i want to dispose this timer
timer.Dispose();
}
catch
{
}
}));
timer.Change(10000, 10000);
EDIT:
I changed the above code slightly according to Chuu's suggestion. Note that if the TimerCallback is called simultanuously by different timer events, Timer.Dispose may end up being called several times. Luckily the Timer does not care if it is being disposed of several times.
Here's a better way to do this. When you use the constructor with only one param (TimerCallback), the state passed to the callback will be the timer itself.
Timer timer = new Timer(o =>
{
((Timer)o).Dispose();
//Your code here
});
//Start the timer
timer.Change(100000,10000);
Here is an example from the msdn docs :
public void StartTimer(int dueTime)
{
Timer t = new Timer(new TimerCallback(TimerProc));
t.Change(dueTime, 0);
}
private void TimerProc(object state)
{
// The state object is the Timer object.
Timer t = (Timer) state;
t.Dispose();
Console.WriteLine("The timer callback executes.");
}
http://msdn.microsoft.com/en-us/library/ms149618(v=vs.110).aspx
You need to keep the reference of the timer in a variable -
public class MyClass
{
private Timer _timer;
public void StartTimer()
{
_timer = new Timer(new TimerCallback(y=>{
try
{
Save(Read(DateTime.Now));
_timer.Dispose();
}
catch {
}
}),null,100000,10000);
}
}
Note: This is untested code. Please check if it works and update.
You'll have to store a reference to the timer somewhere and pass that in as state to the timer object itself. Try creating a class something like this:
public class TimerContainer
{
public Timer Timer { get; set; }
}
Then use it in your method like so:
Action<object> tcb = state =>
{
var container = (TimerConatiner)state;
try
{
Save(Read(DateTime.Now));
container.Timer.Dispose();
}
catch
{
// whatever...
}
};
var container = new TimerContainer();
container.Timer = new Timer(tcb, container, 100000, 10000);
Take care if you use multithreading or multitasking! if so, here you're the code and a solucion for a CancelAfter method extensor (.net 4.0):
private static object syncObj = new object();
public static void CancelAfter(this CancellationTokenSource source, int timeoutMilliseconds, Action code = null)
{
if (timeoutMilliseconds == 0) return; // No timeout
if (source == null)
{
throw new NullReferenceException();
}
if (timeoutMilliseconds < -1)
{
throw new ArgumentOutOfRangeException("timeout");
}
Timer timer = new Timer(delegate(object self)
{
lock (syncObj)
{
try
{
if (null != code)
code.Invoke();
source.Cancel();
((IDisposable)self).Dispose();
}
catch (ObjectDisposedException)
{
}
}
});
timer.Change(timeoutMilliseconds, -1);
}
}
Regards,
Juanlu, ElGuerre

async await not working with Timer

I have a Presence monitor class which is used to detect users active/inactive status. That class has a timer in its Start method which called on application start:
public class PresenceMonitor
{
private volatile bool _running;
private Timer _timer;
private readonly TimeSpan _presenceCheckInterval = TimeSpan.FromMinutes(1);
public PresenceMonitor()
{
}
public void Start()
{
// Start the timer
_timer = new Timer(_ =>
{
Check();
}, null, TimeSpan.Zero, _presenceCheckInterval);
}
private void Check()
{
if (_running)
{
return;
}
_running = true;
// Dowork
}
}
The "Check" method is fired after every one minute. That piece of code is working fine but now my "Do work" methods have become async await so I had to change this Presence Monitor class to something like this:
public class PresenceMonitor
{
private volatile bool _running;
private Timer _timer;
private readonly TimeSpan _presenceCheckInterval = TimeSpan.FromMinutes(1);
public PresenceMonitor()
{
}
public void Start()
{
// Start the timer
var timer = new System.Threading.Timer(async (e) =>
{
await CheckAsync();
}, null, TimeSpan.Zero, _presenceCheckInterval);
}
private async Task CheckAsync()
{
if (_running)
{
return;
}
_running = true;
// await DoworkAsync
}
}
Unfortunately "CheckAsync" method now is getting fired once only instead of every minute. Can you tell me what I am doing wrong here to call async await after regular intervals?
Is there any correct way to do the same?
You could consider creating an event and handler to handle the timer ticks and then invoke your check.
public class PresenceMonitor {
private volatile bool _running;
private Timer timer;
private readonly TimeSpan _presenceCheckInterval = TimeSpan.FromMinutes(1);
public PresenceMonitor() {
Tick += OnTick;
}
public void Start() {
if (_running) {
return; //already running
}
// Start the timer
timer = new System.Threading.Timer(_ => {
Tick(this, EventArgs.Empty);//rasie event
}, null, TimeSpan.Zero, _presenceCheckInterval);
}
private event EventHandler Tick = delegate { };
private async void OnTick(object sender, EventArgs args) {
if (_running) {
return;
}
_running = true;
await DoworkAsync();
}
private Task DoworkAsync() {
//...
}
}
If I understand correctly your requirements, you can get rid of timer and use asynchronous loop.
But you need make Start method asynchronous too
public class PresenceMonitor
{
private volatile bool _running; // possible not needed "volatile" anymore
private readonly int _presenceCheckInterval = 60000; // Milliseconds
public PresenceMonitor()
{
}
public async Task Start()
{
while (true) // may be use some "exit" logic
{
await CheckAsync();
await Task.Delay(_presenceCheckInterval)
}
}
private async Task CheckAsync()
{
if (_running)
{
return;
}
_running = true;
// await DoworkAsync
}
}
Then you can start monitoring
var monitor = new PresenceMonitor();
await monitor.Start();
You can even start monitoring in synchronous way
var monitor = new PresenceMonitor();
monitor.Start(); // Will start monitoring
But approach above is "dangerous" in the way, that any exception thrown inside CheckAsync method will not be propagated. When you start using async-await be ready to "convert" whole application to support it.

Monitor doesn't seem to lock the object

I'm trying to implement a basic Future class (yeah, I know about Task but this is for educational purposes) and ran into strange behavior of Monitor class. The class is implemented so that it enters the lock in constructor, queues an action which exits the lock to a thread pool. Result getter checks an instance variable to see if the action is completed and if it isn't, enters lock and then returns the result. Problem is that in fact result getter doesn't wait for the queued action to finish and proceeds anyway leading to incorrect results. Here's the code.
// The class itself
public class Future<T>
{
private readonly Func<T> _f;
private volatile bool _complete = false;
private T _result;
private Exception _error = new Exception("WTF");
private volatile bool _success = false;
private readonly ConcurrentStack<Action<T>> _callbacks = new ConcurrentStack<Action<T>>();
private readonly ConcurrentStack<Action<Exception>> _errbacks = new ConcurrentStack<Action<Exception>>();
private readonly object _lock = new object();
public Future(Func<T> f)
{
_f = f;
Monitor.Enter(_lock);
ThreadPool.QueueUserWorkItem(Run);
}
public void OnSuccess(Action<T> a)
{
_callbacks.Push(a);
if (_complete && _success)
a(_result);
}
public void OnError(Action<Exception> a)
{
_errbacks.Push(a);
if (_complete && !_success)
a(_error);
}
private void Run(object state)
{
try {
_result = _f();
_success = true;
_complete = true;
foreach (var cb in _callbacks) {
cb(_result);
}
} catch (Exception e) {
_error = e;
_complete = true;
foreach (var cb in _errbacks) {
cb(e);
}
} finally {
Monitor.Exit(_lock);
}
}
public T Result {
get {
if (!_complete) {
Monitor.Enter(_lock);
}
if (_success) {
return _result;
} else {
Console.WriteLine("Throwing error complete={0} success={1}", _complete, _success);
throw _error;
}
}
}
// Failing test
public void TestResultSuccess() {
var f = new Future<int>(() => 1);
var x = f.Result;
Assert.AreEqual (1, x);
}
I'm using Mono 3.2.3 on Mac OS X 10.9.
Only the thread that took the lock can exit the lock. You can't Enter it in the constructor on the calling thread then Exit from the thread-pool when it completes - the thread-pool worker does not have the lock.
And conversely: presumably it is the same thread that created the future that is accessing the getter: that is allowed to Enter again: it is re-entrant. Also, you need to Exit the same number of times that you Enter, otherwise it isn't actually released.
Basically, I don't think Monitor is the right approach here.

Frequent lag spikes in multi-threaded code

I'm attempting to make my simple C# graphics library multi-threaded. However, after the introduction of this code:
/* foreach (IAffector affector in affectorLookup.Values)
affector.Update(timestep); */
taskManager.Value = timestep; taskManager.Start();
foreach (IAffector affector in affectorLookup.Values)
taskManager.AddToQueue(affector.Update);
taskManager.StopWhenDone();
taskManager.Wait();
the simulation starts experiencing sharp lag-spikes, which seem to originate in TaskHandler.Run (I can't tell for sure, because adding the previous code makes my code profiler ignore anything outside TaskHandler.Run).
The task manager:
public class TaskManager
{
public delegate void MethodDel(float timestep);
private Queue<MethodDel> queue;
private List<TaskHandler> handlers;
private float value;
public float Value
{
get
{
return value;
}
set
{
this.value = value;
}
}
public TaskManager()
{
this.queue = new Queue<MethodDel>();
this.handlers = new List<TaskHandler>(System.Environment.ProcessorCount);
for (int t = 0; t < this.handlers.Capacity; ++t)
this.handlers.Add(new TaskHandler(this));
this.value = 0;
}
public void Start()
{
foreach (var handler in handlers)
handler.Start();
}
public void Stop()
{
lock (queue)
queue.Clear();
foreach (var handler in handlers)
handler.StopWhenDone();
}
public void StopWhenDone()
{
foreach (var handler in handlers)
handler.StopWhenDone();
}
public void AddToQueue(MethodDel method)
{
lock (queue)
queue.Enqueue(method);
}
public bool GetFromQueue(out MethodDel method)
{
lock (queue)
{
if (queue.Count == 0) { method = null; return false; }
method = queue.Dequeue();
return true;
}
}
public int GetQueueCount()
{
return queue.Count;
}
internal void Wait()
{
// Have to wait for them one at a time because the main thread is STA.
WaitHandle[] waitHandles = new WaitHandle[1];
// for (int t = 0; t < handlers.Count; ++t)
// waitHandles[t] = handlers[t].WaitHandle;
// WaitHandle.WaitAll(waitHandles);
for (int t = 0; t < handlers.Count; ++t)
{ waitHandles[0] = handlers[t].WaitHandle; WaitHandle.WaitAll(waitHandles); }
}
}
And the task handler:
public class TaskHandler
{
private TaskManager manager;
private Thread thread;
private bool stopWhenDone;
private ManualResetEvent waitHandle;
public ManualResetEvent WaitHandle
{
get
{
return waitHandle;
}
}
public TaskHandler(TaskManager manager)
{
this.manager = manager;
}
public void Start()
{
waitHandle = new ManualResetEvent(false);
stopWhenDone = false;
thread = new Thread(Run);
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.MTA);
thread.Start();
}
public void StopWhenDone()
{
this.stopWhenDone = true;
}
// Possible source of slowdown
private void Run()
{
TaskManager.MethodDel curMethod;
while (!stopWhenDone || manager.GetQueueCount() > 0)
{
if (manager.GetFromQueue(out curMethod))
{
curMethod(manager.Value);
}
}
waitHandle.Set();
}
}
Starting a thread is a heavy operation. Not sure if it's as heavy as you are experiencing, but that could be it. Also, having all your processing run parallel can be putting a big strain on your system with possibly little benefit...
I'm going to venture that the spikes have something to do with waitHandle.Set();
I like the overall design, but I have not used WaitHandle before, so I am unsure how this interacts with your design.

How does a Timer Elapsed event compete with High Priority threads?

I have a System.Timers.Timer object that I want to use, but I don't want the processing tied to the Timer to interfere with normal to high priority threads. In other words, I'd like to say that I want to process X every 5 seconds as long as nothing else is running.
How could I ensure that my Timer operations are running in a low-priority manner?
The nice thing about System.Timers.Timer is that you can assign a synchronzing object via the SynchronizingObject property and then exploit it to run the Elapsed event a thread whose priority can be controlled.
Just assign an instance of the ElapsedEventReceiver to the SynchronizingObject property of your timer.
Disclaimer: I whipped this up pretty fast so you will need to add your own finishing touches to make it more robust.
public class ElapsedEventReceiver : ISynchronizeInvoke
{
private Thread m_Thread;
private BlockingCollection<Message> m_Queue = new BlockingCollection<Message>();
public ElapsedEventReceiver()
{
m_Thread = new Thread(Run);
m_Thread.Priority = ThreadPriority.BelowNormal;
m_Thread.IsBackground = true;
m_Thread.Start();
}
private void Run()
{
while (true)
{
Message message = m_Queue.Take();
message.Return = message.Method.DynamicInvoke(message.Args);
message.Finished.Set();
}
}
public IAsyncResult BeginInvoke(Delegate method, object[] args)
{
Message message = new Message();
message.Method = method;
message.Args = args;
m_Queue.Add(message);
return message;
}
public object EndInvoke(IAsyncResult result)
{
Message message = result as Message;
if (message != null)
{
message.Finished.WaitOne();
return message.Return;
}
throw new ArgumentException("result");
}
public object Invoke(Delegate method, object[] args)
{
Message message = new Message();
message.Method = method;
message.Args = args;
m_Queue.Add(message);
message.Finished.WaitOne();
return message.Return;
}
public bool InvokeRequired
{
get { return Thread.CurrentThread != m_Thread; }
}
private class Message : IAsyncResult
{
public Delegate Method;
public object[] Args;
public object Return;
public object State;
public ManualResetEvent Finished = new ManualResetEvent(false);
public object AsyncState
{
get { return State; }
}
public WaitHandle AsyncWaitHandle
{
get { return Finished; }
}
public bool CompletedSynchronously
{
get { return false; }
}
public bool IsCompleted
{
get { return Finished.WaitOne(0); }
}
}
}
Probably the easiest way would be to have a "busy" flag (or count), and ignore the timer ticks as long as it's non-zero.
P.S. Changing thread priorities is not recommended. It's hardly ever necessary.

Categories