async await not working with Timer - c#

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.

Related

I want to run a method every minute that's started from my OnAppearing(). Do I need to run this as a task?

Here's the code that I have come up with. It seems to work but I am concerned that it may not be good way to do what I want to do. What I need is to run a method every minute as soon as the OnAppearing happens and have it stop with the OnDisappearing();
protected async override void OnAppearing()
{
base.OnAppearing();
BindingContext = vm;
cts = new CancellationTokenSource();
if (Settings.mode == MO.Practice)
{
if (!App.stopWatch.IsRunning) { App.stopWatch.Start(); }
Device.StartTimer(new TimeSpan(0, 0, 5), () =>
{
if (App.stopWatch.IsRunning && App.stopWatch.Elapsed.Seconds >= 60)
{
// Here's the method I want to run. After it's finished
// I call BeginInvoke .. to update info on the screen
if (App.DB.ReducePoints() == true)
Device.BeginInvokeOnMainThread(() =>
{
vm.PifInfo = GetPifInfo();
});
App.stopWatch.Restart();
}
return true;
});
}
await GetCards(cts.Token);
}
}
protected override void OnDisappearing()
{
Unsubscribe();
cts.Cancel();
if (App.stopWatch.IsRunning) { App.stopWatch.Stop(); }
base.OnDisappearing();
}
Not part of the question but I would welcome any comments on the code also.
You can do this simpler by returning the correct value from Device.StartTimer, to repeat true, to not repeat false and not use a StopWatch. (source states that While the callback returns true, the timer will keep recurring. And as you see from the source, the method doesn't need a Func<Task<bool>> it only needs a Func<bool> callback so there is no need to use a Task.)
in the class
volatile bool run;
in OnAppearing
run = true;
Device.StartTimer(new TimeSpan(0, 1, 0), () => {
if (run) { /*do what you want;*/ return true; }
else { return false; }
});
in OnDisappearing
run = false;
EDIT - as requested by OP
Here is the code. I am leaving my original answer to help anyone else who needs this.
volatile bool run;
protected async override void OnAppearing()
{
base.OnAppearing();
BindingContext = vm;
cts = new CancellationTokenSource();
if (Settings.mode == MO.Practice)
{
run = true;
Device.StartTimer(new TimeSpan(0, 1, 0), () =>
{
if (run)
{
if (App.DB.ReducePoints() == true)
Device.BeginInvokeOnMainThread(() =>
{
vm.PifInfo = GetPifInfo();
});
return true;
}
else { return false; }
});
await GetCards(cts.Token);
}
}
protected override void OnDisappearing()
{
run = false;
Unsubscribe();
cts.Cancel();
base.OnDisappearing();
}
You can refactor the code to make proper use of the Timer in combination with a CancellationToken.
Also note the use of the async event handler to avoid the fire and forget call to async void OnAppearing that won't allow thrown exceptions to be caught and can cause crashes.
CancellationTokenSource source;
protected override void OnAppearing() {
base.OnAppearing();
BindingContext = vm;
timerStarted += onTimerStarted;
timerStarted(this, EventArgs.Empty);
}
private event EventHandler timerStarted = delegate { };
private async void onTimerStarted(object sender, EventArgs args) {
timerStarted -= onTimerStarted;
cts = new CancellationTokenSource();
if (Settings.mode == MO.Practice) {
source = new CancellationTokenSource();
StartTimer(source.Token);
await GetCards(cts.Token);
}
}
private void StartTimer(CancellationToken token) {
var interval = TimeSpan.FromMinutes(1);
Func<bool> callback = () => {
//check if to stop timer
if(token.IsCancellationRequested) return false;
//Code to be repeated
checkPoints();
//While the callback returns true, the timer will keep recurring.
return true;
};
//repeat this function every minute
Device.StartTimer(interval, callback);
}
private void checkPoints() {
// Here's the method I want to run. After it's finished
// I call BeginInvoke .. to update info on the screen
if (App.DB.ReducePoints() == true) {
Device.BeginInvokeOnMainThread(() => {
vm.PifInfo = GetPifInfo();
});
}
}
protected override void OnDisappearing() {
source.Cancel();//Timer will short-circuit on next interval
Unsubscribe();
cts.Cancel();
base.OnDisappearing();
}
The cancellation token will be used to force the timer to return false and stop recurring when the token is cancelled in OnDisappearing().
If the function to be repeated needs to be asynchronous add another async event handler to manage that.
You can use Rx.Timer for that:
protected async override void OnAppearing()
{
_sub = Observable.Timer(0, TimeSpan.FromMinutes(1))
.ObserveOnDispatcher() // move invocation to dispatcher
.Do(_ => {
vm.PifInfo = GetPifInfo(); // do your work. Try/catch will be usefull, otherwise any exception will break the subscription
})
// .Retry() // uncomment this if you want to immedietally try again after a failure, no try/catch then
.Subscribe();
....
}
protected override void OnDisappearing()
{
_sub?.Dispose(); // this will unsubscribe to the timer
...
}
try creating a new class that is responsible to do the timed check.
with a boolean property continueChecking. It does a sleep, then calls a different method to do the work, then checks if it has to continue checking. if so, it calls itself.
In the onDisappearing you set the continueChecking to false, so the method stops calling itself.
By doing so you split the concern of starting and ending the loop, the loop itself and the work it has to do in three separate places.
Edit: code example
public class ButtonClicked
{
//make sure you've got the same instance on both methods
private Loop loop = new Loop();
protected async void OnAppearing()
{
//other work
await loop.StartLoop();
}
protected void OnDisappearing()
{
//other work
loop.StopLoop();
}
}
public class Loop
{
private bool _continueChecking;
private readonly TimeSpan interval = new TimeSpan(0,1,0);
public async Task StartLoop()
{
await DoLoop();
}
public void StopLoop()
{
_continueChecking = false;
}
private async Task DoLoop()
{
_continueChecking = true;
await Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(interval);
TheWork();
});
if (_continueChecking)
{
DoLoop();
}
}
private void TheWork()
{
//specific work stuff
//can be anywhere so it is testable and reusable
}
}

C# One Time (Fire once) Events Implementation

I'm interested creating an event handling object that you can subscribe for one time execution only and then the action is automatically unsubscribed
Is there similar native functionality in .NET?
Here is what works for me right now:
public class CustomTimer
{
private event Action OneSecond;
private readonly Timer timer;
// Registered actions that should be called only once
private readonly ICollection<Action> oneOffs;
public CustomTimer()
{
this.timer = new Timer { Interval = 1000 };
this.timer.Elapsed += this.OnOneSecond;
this.oneOffs = new HashSet<Action>();
}
public bool IsRunning => this.timer.Enabled;
public void Start()
{
this.timer.Start();
}
public void Stop()
{
this.timer.Stop();
}
public void Subscribe(Action callback)
{
this.OneSecond += callback;
}
public void SubscribeOnce(Action callback)
{
this.oneOffs.Add(callback);
this.Subscribe(callback);
}
public void Unsubscribe(Action callback)
{
this.OneSecond -= callback;
this.oneOffs.Remove(callback);
}
protected virtual void OnOneSecond(object sender, ElapsedEventArgs elapsedEventArgs)
{
this.OneSecond?.Invoke();
this.UnsubscribeOneOffs();
}
private void UnsubscribeOneOffs()
{
if (this.oneOffs.Count > 0)
{
foreach (var action in this.oneOffs)
{
this.OneSecond -= action;
}
this.oneOffs.Clear();
}
}
}
Here the events are set to execute every second.
How can I use similar strategy in other object that trigger events unpredictably
and prevent events execution while the UnsubscribeOneOffs() method is running.
Should I use some kind of lock?
There is no need to register one time actions as OneSecond event handlers. Just keep them in a separate list.
public class CustomTimer
{
List<Action> _oneTimeActions = new List<Action>();
public void SubscribeOnce(Action handler)
{
lock(_oneTimeActions)
{
_oneTimeActions.Add(handler);
}
}
protected virtual void OnOneSecond(object sender, ElapsedEventArgs elapsedEventArgs)
{
// get a local copy of scheduled one time items
// removing them from the list.
Action[] oneTimers;
lock(_oneTimeActions)
{
oneTimers = _oneTimeActions.ToArray();
_oneTimeActions.Clear();
}
// Execute periodic events first
this.OneSecond?.Invoke();
// Now execute one time actions
foreach(var action in oneTimers)
{
action();
}
}
}

Timer is firing and executing even the first one is not finished?

I have a windows service application. I want this windows service to run a method after every x minutes. I also want when the existing method is running, it should not allow the timer method to execute. I have,
private static readonly ILogger Logger = GetLogger();
private Timer _timer;
protected override void OnStart(string[] args)
{
Logger.Log("Starting Timer");
StartTimer();
}
protected override void OnStop()
{
Logger.Log("Stoping Timer");
StopTimer();
}
private void Timer_Elapsed(object state)
{
Logger.Log("Timer Elapsed");
DoWork();
RestartTimer();
}
private static void DoWork()
{
try
{
// Doing my work there
}
catch (Exception ex)
{
Logger.Log(ex);
}
}
private void StartTimer()
{
Logger.Log("Running first time manually");
ThreadPool.QueueUserWorkItem((_) => DoWork());
var frequency = GetTimerFrequency();
_timer = new Timer(Timer_Elapsed, null, frequency, Timeout.Infinite);
}
private void RestartTimer()
{
var frequency = GetTimerFrequency();
_timer.Change(frequency, Timeout.Infinite);
}
private void StopTimer()
{
_timer.Change(Timeout.Infinite, Timeout.Infinite);
_timer.Dispose();
_timer = null;
}
I dunno why but some-times when the method DoWork is running, Timer_Elapsed is executing. I want until one DoWork finished, no more DoWork is allowed to execute.
Possible Duplicate of this post. You have to check the status of the app whether its already running or not.
Here

Joining a thread started with StartNew()

When using the StartNew() method to kick off a process on a new thread, I need to figure out how to make another call into this object in that same thread (I assume this would be some sort of Join operation?).
The following example is dumbed down to illustrate the meat of what I am trying to do. I am well aware it is severely lacking in basic concurrency considerations. But I didn't want to cloud the code with all of that logic, so please forgive me on that.
The following console app shows what I am trying to accomplish. Assume on the StartNew() call a new thread with ID 9976 is created and the method invoked there. I would like the subsequent call to ProcessImmediate() in the file system watcher change event handler to be made on thread 9976 as well. As it stands, the call would share the same thread that is used for the file system watcher change event.
Can this be done, and if so, how?
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var runner = new Runner();
runner.Run();
Console.ReadKey();
}
}
public class Runner
{
private Activity _activity = null;
private FileSystemWatcher _fileSystemWatcher;
public void Run()
{
_activity = new Activity();
// start activity on a new thread
Task.Factory.StartNew(() => _activity.Go());
_fileSystemWatcher = new FileSystemWatcher();
_fileSystemWatcher.Filter = "*.watcher";
_fileSystemWatcher.Path = "c:\temp";
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
_fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
// WANT TO CALL THIS FOR ACTIVITY RUNNING ON PREVIOUSLY CALLED THREAD
_activity.ProcessImmediate();
}
}
public class Activity
{
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
System.Threading.Thread.Sleep(2000);
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate()
{
// for purposes of this example, assume that Go is magically in its sleep state when ProcessImmediate is called
DoSomethingInteresting();
}
public bool Stop { get; set; }
}
}
* UPDATE *
Thanks for the excellent responses. I took Mike's suggestion and implemented it for my console app. Below is the full working code which also includes the use of a cancellation token. I post this in case someone else might find it useful.
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var runner = new Runner();
runner.Run();
Console.ReadKey();
runner.Stop();
Console.ReadKey();
}
}
public class Runner
{
private Activity _activity = null;
private FileSystemWatcher _fileSystemWatcher;
private CancellationTokenSource _cts = new CancellationTokenSource();
public void Stop() { _cts.Cancel(); }
public void Run()
{
_activity = new Activity();
// start activity on a new thread
var task = new Task(() => _activity.Go(_cts.Token), _cts.Token, TaskCreationOptions.LongRunning);
task.Start();
_fileSystemWatcher = new FileSystemWatcher();
_fileSystemWatcher.Filter = "*.watcher";
_fileSystemWatcher.Path = "C:\\Temp\\FileSystemWatcherPath";
_fileSystemWatcher.Changed += FileSystemWatcher_Changed;
_fileSystemWatcher.EnableRaisingEvents = true;
}
private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
// WANT TO CALL THIS FOR ACTIVITY RUNNING ON PREVIOUSLY CALLED THREAD
_activity.ProcessImmediate();
}
}
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
public void Go(CancellationToken ct)
{
Thread.CurrentThread.Name = "Go";
while (!ct.IsCancellationRequested)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(5000);
}
Console.WriteLine("Exiting");
}
protected virtual void DoSomethingInteresting()
{
Console.WriteLine(string.Format("Doing Something Interesting on thread {0}", Thread.CurrentThread.ManagedThreadId));
}
public void ProcessImmediate()
{
// for purposes of this example, assume that Go is magically in its sleep state when ProcessImmediate is called
_processing.Set();
}
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
}
First, you should use TaskCreationOptions.LongRunning if you are creating a task that will not complete quickly. Second, use an AutoResetEvent to signal the waiting thread to wake up. Note that below ProcessImmediate will return before DoSomethingInteresting has completed running on the other thread. Example:
using System.Threading;
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(2000);
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate()
{
_processing.Set();
}
public bool Stop { get; set; }
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
User mike has given a better solution, which will be appropriate when you like to call the same method immediately. If you want to call a different methods immediately I'll expand mike's answer to achieve that.
using System.Threading;
public class Activity : IDisposable
{
private AutoResetEvent _processing = new AutoResetEvent(false);
private ConcurrentQueue<Action> actionsToProcess = new ConcurrentQueue<Action>();
public void Go()
{
while (!Stop)
{
// for purposes of this example, magically assume that ProcessImmediate has not been called when this is called
DoSomethingInteresting();
_processing.WaitOne(2000);
while(!actionsToProcess.IsEmpty)
{
Action action;
if(actionsToProcess.TryDeque(out action))
action();
}
}
}
protected virtual void DoSomethingInteresting() { }
public void ProcessImmediate(Action action)
{
actionsToProcess.Enqueue(action);
_processing.Set();
}
public bool Stop { get; set; }
public void Dispose()
{
if (_processing != null)
{
_processing.Dispose();
_processing = null;
}
}
}
To execute different methods on the same thread you can use a message loop that dispatches incoming requests. A simple option would be to use the event loop scheduler of the Reactive Extensions and to "recursively" schedule your Go() function - if in the mean time a different operation is scheduled it would be processed before the next Go() operation.
Here is a sample:
class Loop
: IDisposable
{
IScheduler scheduler = new EventLoopScheduler();
MultipleAssignmentDisposable stopper = new MultipleAssignmentDisposable();
public Loop()
{
Next();
}
void Next()
{
if (!stopper.IsDisposed)
stopper.Disposable = scheduler.Schedule(Handler);
}
void Handler()
{
Thread.Sleep(1000);
Console.WriteLine("Handler: {0}", Thread.CurrentThread.ManagedThreadId);
Next();
}
public void Notify()
{
scheduler.Schedule(() =>
{
Console.WriteLine("Notify: {0}", Thread.CurrentThread.ManagedThreadId);
});
}
public void Dispose()
{
stopper.Dispose();
}
}
static void Main(string[] args)
{
using (var l = new Loop())
{
Console.WriteLine("Press 'q' to quit.");
while (Console.ReadKey().Key != ConsoleKey.Q)
l.Notify();
}
}

GUI froze while Dispatcher.BeginInvoke or Task.StartNew

NOTE: Depends on this quetion
I have a view-model like this:
public class ViewModel {
private readonly IPersonService _personService;
private readonly ObservableCollection<SearchPersonModel> _foundedList;
private readonly DispatcherTimer _timer;
private readonly Dispatcher _dispatcher;
private CancellationTokenSource _tokenSource;
public SearchPatientViewModel(IPersonService personService) {
_personService = personService;
_foundedList = new ObservableCollection<SearchPersonModel>();
_dispatcher = (/*CurrentApplication*/).Dispatcher;
_timer = new DispatcherTimer(
TimeSpan.FromMilliseconds(1000),
DispatcherPriority.Normal,
TimerCallBack,
_dispatcher);
_tokenSource = new CancellationTokenSource();
}
public string Term {
get { return _term; }
set {
// implementing INotifyPropertyChanged
if(_term== value)
return;
_term= value;
OnPropertyChanged(() => Term);
tokenSource.Cancel(); // canceling prev search query
_timer.Stop(); // stop the timer to reset it
// start it again to do a search query if user change not the term for 1000ms
_timer.Start();
}
}
private void TimerCallBack(object sender, EventArgs e) {
_timer.Stop();
_tokenSource = new CancellationTokenSource();
var task = Task<IEnumerable<SearchPersonModel>>.Factory
.StartNew(Search, _tokenSource.Token);
_dispatcher.BeginInvoke((Action)(() => {
_foundedList.Clear();
foreach(var item in task.Result)
_foundedList.Add(item);
}), DispatcherPriority.Background);
}
private IEnumerable<SearchPersonModel> Search() {
return _personService.DoSearch(this.Term);
}
}
and in the IPersonService implementation I do this:
public class PersonService : IPersonService {
public IEnumerable<SearchPersonModel> DoSearch(string term){
System.Threading.Thread.Sleep(10000);
return some-search-result;
}
}
However, I expect that while search query is executing, GUI be free. But it froze! Have you any idea where is my mistake?
The problem is that evaluating task.Result will block until the query has completed.
The simplest option is probably to make the Search method perform the _dispatcher.BeginInvoke call at the end instead.
Another option - which will become easier with C# 5 - would be to add a continuation to the task, so that when it's completed you can update the UI. At the moment you'd use Task.ContinueWith; with C# 5 you'd use async and await.

Categories