I have a server which listens to clients in a while(true) loop.
I keep every client's hostname in a list and save the time the client contact the server.
I would like to check every 10 minutes if some of the clients didn't contact the server in the last hour and to print its name.
I thought about doing something like this:
Task.Run(CheckTheClients()) //Check the passed-time of each client in the list
while(true)
{
//listen to clients, add them to list, etc.
}
But I'm not sure how to do the check every 10 minutes and not every millisecond, neither if my idea is good or not.
So What is the best way to do this?
Moreover, both the function and the while(true) touches the list of the clients. Is that going to make some problems?
This would be best done by using the Timer function, basically you create it, pass it a function to call at each amount of time passed, and set the time to wait in Milliseconds. So for your example of 10 minutes, something like this:
// insert this into a long running function, and scope the timer variable correctly
System.Timers.Timer myTimer = new System.Timers.Timer();
myTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
myTimer.Interval = 600000;
myTimer.Enabled = true;
// Define what you want to happen when the Elapsed event occurs (happens on the interval you set).
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
//do some work here
}
You can put your thread to sleep, like this:
while (true)
{
try
{
// do something
}
catch (Exception ex)
{
// save log
}
Thread.Sleep(TimeSpan.FromMilliseconds(TimeSpan.FromMinutes(10).TotalMilliseconds));
}
Since you're using Task.Run in the sample code you provided, why not use Task.Delay while you're at it?
Action CheckTheClients = () => Console.WriteLine("Checking clients...");
while (true)
{
var task = Task.Delay(1000).ContinueWith(x => CheckTheClients());
await task;
}
No need to sign up for timer and its events, if you're going for simplicity.
Related
I want to call a method after some delay when an event is raised, but any subsequent events should "restart" this delay. Quick example to illustrate, the view should be updated when scrollbar position changes, but only 1 second after the user has finished scrolling.
Now I can see many ways of implementing that, but the most intuitive would be to use Task.Delay + ContinueWith + cancellation token. However, I am experiencing some issues, more precisely subsequent calls to my function cause the TaskCanceledException exception and I started to wonder how I could get rid of that. Here is my code:
private CancellationTokenSource? _cts;
private async void Update()
{
_cts?.Cancel();
_cts = new();
await Task.Delay(TimeSpan.FromSeconds(1), _cts.Token)
.ContinueWith(o => Debug.WriteLine("Update now!"),
TaskContinuationOptions.OnlyOnRanToCompletion);
}
I have found a workaround that works pretty nicely, but I would like to make the first idea work.
private CancellationTokenSource? _cts;
private CancellationTokenRegistration? _cancellationTokenRegistration;
private void Update()
{
_cancellationTokenRegistration?.Unregister();
_cts = new();
_cancellationTokenRegistration = _cts.Token.Register(() => Debug.WriteLine("Update now!"));
_cts.CancelAfter(1000);
}
You should consider using Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq;.
You didn't say hat UI you're using, so for Windows Forms also add System.Reactive.Windows.Forms and for WPF System.Reactive.Windows.Threading.
Then you can do this:
Panel panel = new Panel(); // assuming this is a scrollable control
IObservable<EventPattern<ScrollEventArgs>> query =
Observable
.FromEventPattern<ScrollEventHandler, ScrollEventArgs>(
h => panel.Scroll += h,
h => panel.Scroll -= h)
.Select(sea => Observable.Timer(TimeSpan.FromSeconds(1.0)).Select(_ => sea))
.Switch();
IDisposable subscription = query.Subscribe(sea => Console.WriteLine("Hello"));
The query is firing for every Scroll event and starts a one second timer. The Switch operator watches for every Timer produces and only connects to the latest one produced, thus ignoring the previous Scroll events.
And that's it.
After scrolling has a 1 second pause the word "Hello" is written to the console. If you begin scrolling again then after every further 1 second pause it fires again.
In my own experience I've dealt with lots of scenarios just like the one you describe, e.g. update something one second after the mouse stops moving etc.
For a long time I would do timer restarts just the way you describe, by cancelling an old task and starting a new one. But I never really liked how messy that was, so I came up with an alternative that I use in production code. Long-term it has proven quite reliable. It takes advantage of the captured context associated with a task. Multiple instances of TaskCanceledException no longer occur.
class WatchDogTimer
{
int _wdtCount = 0;
public TimeSpan Interval { get; set; } = TimeSpan.FromSeconds(1);
public void Restart(Action onRanToCompletion)
{
_wdtCount++;
var capturedCount = _wdtCount;
Task
.Delay(Interval)
.GetAwaiter()
.OnCompleted(() =>
{
// If the 'captured' localCount has not changed after awaiting the Interval,
// it indicates that no new 'bones' have been thrown during that interval.
if (capturedCount.Equals(_wdtCount))
{
onRanToCompletion();
}
});
}
}
Another nice perk is that it doesn't rely on platform timers and works just as well in iOS/Android as it does in WinForms/WPF.
For purposes of demonstration, this can be exercised in a quick console demo where the MockUpdateView() action is sent to the WDT 10 times at 500 ms intervals. It will only execute one time, 500 ms after the last restart is received.
static void Main(string[] args)
{
Console.Title = "Test WDT";
var wdt = new WatchDogTimer { Interval = TimeSpan.FromMilliseconds(500) };
Console.WriteLine(DateTime.Now.ToLongTimeString());
// "Update view 500 ms after the last restart."
for (int i = 0; i < 10; i++)
{
wdt.Restart(onRanToCompletion: ()=>MockUpdateView());
Thread.Sleep(TimeSpan.FromMilliseconds(500));
}
Console.ReadKey();
}
static void MockUpdateView()
{
Console.WriteLine($"Update now! WDT expired {DateTime.Now.ToLongTimeString()}");
}
}
So, with 500 ms times 10 restarts this verifies one event at 5 seconds from the start.
You can combine a state variable and a delay to avoid messing with timers or task cancelation. This is far simpler IMO.
Add this state variable to your class/form:
private DateTime _nextRefresh = DateTime.MaxValue;
And here's how you refresh:
private async void Update()
{
await RefreshInOneSecond();
}
private async Task RefreshInOneSecond()
{
_nextRefresh = DateTime.Now.AddSeconds(1);
await Task.Delay(1000);
if (_nextRefresh <= DateTime.Now)
{
_nextRefresh = DateTime.MaxValue;
Refresh();
}
}
If you call RefreshInOneSecond repeatedly, it pushes out the _nextRefresh timestamp until later, so any refreshes already in flight will do nothing.
Demo on DotNetFiddle
One approach is to create a timer and reset this whenever the user does something. For example using System.Timers.Timer
timer = new Timer(1000);
timer.SynchronizingObject = myControl; // Needs a winforms object for synchronization
timer.Elapsed += OnElapsed;
timer.Start(); // Don't forget to stop the timer whenever you are done
...
private void OnUserUpdate(){
timer.Interval = 1000; // Setting the interval will reset the timer
}
There are multiple timers to chose from, I believe the same pattern is possible with the other timers. DispatchTimer might be most suitable if you use WPF.
Note that both System.Timers.Timer and Task.Delay uses System.Threading.Timer in the background. It is possible to use this directly, just call the .Change method to reset it. But be aware that this raises the event on a taskpool thread, so you need to provide your own synchronization.
I implemented the same scenario in a JavaScript application using Timer. I believe it's the same in the .NET world. Anyway handling this use-case when the user calls a method repeatedly with Task.Delay() will put more pressure on GC & thread pool
var timer = new Timer()
{
Enabled = true,
Interval = TimeSpan.FromSeconds(5).TotalMilliseconds,
};
timer.Elapsed += (sender, eventArgs) =>
{
timer.Stop();
// do stuff
}
void OnKeyUp()
{
timer.Stop();
timer.Start();
}
I want to execute a function every 60 seconds in C#. I could use the Timer class like so:
timer1 = new Timer();
timer1.Tick += new EventHandler(timer1_Tick);
timer1.Interval = 60 * 1000; // in miliseconds
timer1.Start();
Question is I have a long running process. Occasionally it make take several minutes. Is there a way to make the timer smart so if the function is already being executed then it should skip that cycle and come back 60 seconds later and if again it is in execution then again skip and come back 60 seconds later.
I would suggest you to have a class member variable bool variable with value false.
then in click event return if its true at the beginning.
and then set it to true, so that it will tell you that its currently in execution.
then write your logic.
and then once done finally set it to false again.
code will look like this.
private bool isRunning = false;
private void timer1_Tick(object sender, EventArgs e)
{
if (isRunning)
{
return;
}
isRunning = true;
try
{
... //Do whatever you want
}
finally
{
isRunning = false;
}
}
The modern and most clean way to do this is using Microsoft's new Period Timer:
var timer = new PeriodicTimer(TimeSpan.FromSeconds(n));
while (await timer.WaitForNextTickAsync())
{
//Business logic
}
If you need to abort such a ticker, you can pass a cancellation token to the WaitForNextTickAsync method.
Another advantage is this:
The PeriodicTimer behaves like an auto-reset event, in that multiple ticks are coalesced into a single tick if they occur between calls to WaitForNextTickAsync(CancellationToken). Similarly, a call to Dispose() will void any tick not yet consumed. WaitForNextTickAsync(CancellationToken) may only be used by one consumer at a time, and may be used concurrently with a single call to Dispose().
Source: https://learn.microsoft.com/en-us/dotnet/api/system.threading.periodictimer.waitfornexttickasync?source=recommendations&view=net-7.0#remarks
If you need more granularity (like "always at 10 am", use something like https://github.com/HangfireIO/Cronos
Use a timer, set it to 60 second
On Event:
try
Stop timer
Do logic
catch
What ever fail recovery
finally
Start the timer
Logic is run 60 seconds after last finish.
You can use a Stopwatch inside a loop: start the stopwatch, after 60 second call the function, reset the stopwatch, start the loop again.
I have a service written in C# (.NET 1.1) and want it to perform some cleanup actions at midnight every night. I have to keep all code contained within the service, so what's the easiest way to accomplish this? Use of Thread.Sleep() and checking for the time rolling over?
I wouldn't use Thread.Sleep(). Either use a scheduled task (as others have mentioned), or set up a timer inside your service, which fires periodically (every 10 minutes for example) and check if the date changed since the last run:
private Timer _timer;
private DateTime _lastRun = DateTime.Now.AddDays(-1);
protected override void OnStart(string[] args)
{
_timer = new Timer(10 * 60 * 1000); // every 10 minutes
_timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
_timer.Start();
//...
}
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
// ignore the time, just compare the date
if (_lastRun.Date < DateTime.Now.Date)
{
// stop the timer while we are running the cleanup task
_timer.Stop();
//
// do cleanup stuff
//
_lastRun = DateTime.Now;
_timer.Start();
}
}
Check out Quartz.NET. You can use it within a Windows service. It allows you to run a job based on a configured schedule, and it even supports a simple "cron job" syntax. I've had a lot of success with it.
Here's a quick example of its usage:
// Instantiate the Quartz.NET scheduler
var schedulerFactory = new StdSchedulerFactory();
var scheduler = schedulerFactory.GetScheduler();
// Instantiate the JobDetail object passing in the type of your
// custom job class. Your class merely needs to implement a simple
// interface with a single method called "Execute".
var job = new JobDetail("job1", "group1", typeof(MyJobClass));
// Instantiate a trigger using the basic cron syntax.
// This tells it to run at 1AM every Monday - Friday.
var trigger = new CronTrigger(
"trigger1", "group1", "job1", "group1", "0 0 1 ? * MON-FRI");
// Add the job to the scheduler
scheduler.AddJob(job, true);
scheduler.ScheduleJob(trigger);
A daily task? Sounds like it should just be a scheduled task (control panel) - no need for a service here.
Does it have to be an actual service? Can you just use the built in scheduled tasks in the windows control panel.
The way I accomplish this is with a timer.
Run a server timer, have it check the Hour/Minute every 60 seconds.
If it's the right Hour/Minute, then run your process.
I actually have this abstracted out into a base class I call OnceADayRunner.
Let me clean up the code a bit and I'll post it here.
private void OnceADayRunnerTimer_Elapsed(object sender, ElapsedEventArgs e)
{
using (NDC.Push(GetType().Name))
{
try
{
log.DebugFormat("Checking if it's time to process at: {0}", e.SignalTime);
log.DebugFormat("IsTestMode: {0}", IsTestMode);
if ((e.SignalTime.Minute == MinuteToCheck && e.SignalTime.Hour == HourToCheck) || IsTestMode)
{
log.InfoFormat("Processing at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
OnceADayTimer.Enabled = false;
OnceADayMethod();
OnceADayTimer.Enabled = true;
IsTestMode = false;
}
else
{
log.DebugFormat("Not correct time at: Hour = {0} - Minute = {1}", e.SignalTime.Hour, e.SignalTime.Minute);
}
}
catch (Exception ex)
{
OnceADayTimer.Enabled = true;
log.Error(ex.ToString());
}
OnceADayTimer.Start();
}
}
The beef of the method is in the e.SignalTime.Minute/Hour check.
There are hooks in there for testing, etc. but this is what your elapsed timer could look like to make it all work.
As others already wrote, a timer is the best option in the scenario you described.
Depending on your exact requirements, checking the current time every minute may not be necessary.
If you do not need to perform the action exactly at midnight, but just within one hour after midnight, you can go for Martin's approach of only checking if the date has changed.
If the reason you want to perform your action at midnight is that you expect a low workload on your computer, better take care: The same assumption is often made by others, and suddenly you have 100 cleanup actions kicking off between 0:00 and 0:01 a.m.
In that case you should consider starting your cleanup at a different time. I usually do those things not at clock hour, but at half hours (1.30 a.m. being my personal preference)
I would suggest that you use a timer, but set it to check every 45 seconds, not minute. Otherwise you can run into situations where with heavy load, the check for a particular minute is missed, because between the time the timer triggers and the time your code runs and checks the current time, you might have missed the target minute.
You can also try the TaskSchedulerLibrary here http://visualstudiogallery.msdn.microsoft.com/a4a4f042-ffd3-42f2-a689-290ec13011f8
Implement the abstract class AbstractScheduledTask and call the ScheduleUtilityFactory.AddScheduleTaskToBatch static method
For those that found the above solutions not working, it's because you may have a this inside your class, which implies an extension method which, as the error message says, only makes sense on a non-generic static class. Your class isn't static. This doesn't seem to be something that makes sense as an extension method, since it's acting on the instance in question, so remove the this.
Try this:
public partial class Service : ServiceBase
{
private Timer timer;
public Service()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
SetTimer();
}
private void SetTimer()
{
if (timer == null)
{
timer = new Timer();
timer.AutoReset = true;
timer.Interval = 60000 * Convert.ToDouble(ConfigurationManager.AppSettings["IntervalMinutes"]);
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
}
private void timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
{
//Do some thing logic here
}
protected override void OnStop()
{
// disposed all service objects
}
}
I want to run a process every one minute, but I have been told that the Timer is working every x minute + the time required for the process to finish. but I want the thread to work every 1 minute even though the thread process may keep working for 1 hour.
I hope you got me, so in the final image, I may have 10 threads working together.
is that possible ?
Depends on the timer. Simple test shows that System.Threading.Timer works the way you want:
var timer = new Timer(s => { "Start".Dump(); Thread.Sleep(10000); "Hi!".Dump(); },
null, 1000, 1000);
Thread.Sleep(20000);
timer.Dump();
The callback executes every second even though it takes ten seconds to execute.
This is basically because the callback for this particular timer is simply posted to the threadpool, while e.g. System.Windows.Forms.Timer is actually tied to the UI thread. Of course, if you simply start a new thread (or queue work, or start a new task etc.) in the callback of winforms timer, it will work in a similar (albeit less precise) way.
Using the right tool for the job usually makes things much easier :)
Create a Timer and on the elapse event just fire a new thread to do the work, like the below example:
public class Example
{
private static Timer aTimer;
public static void Main()
{
// Create a timer with a two second interval.
aTimer = new Timer(2000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += OnTimedEvent;
aTimer.Enabled = true;
Console.WriteLine("Press the Enter key to exit the program... ");
Console.ReadLine();
Console.WriteLine("Terminating the application...");
}
public static void DoWork()
{
var workCounter = 0;
while (workCounter < 100)
{
Console.WriteLine("Alpha.Beta is running in its own thread." + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
workCounter++;
}
}
private static void OnTimedEvent(Object source, ElapsedEventArgs e)
{
// Create the thread object, passing in the method
// via a delegate.
var oThread = new Thread(DoWork);
// Start the thread
oThread.Start();
}
}
Since .NET 4.0 Tasks are preferred to Threads.
The overhead of Task management is minimal.
// Create a task spawning a working task every 1000 msec
var t = Task.Run(async delegate
{
while (isRunning)
{
await Task.Delay(1000);
Task.Run(() =>
{
//your work
};
}
});
I have mainly been reusing a code snippet from old times:
public void Start()
{
renewalThread = new Thread(() =>
{
while (!disposed)
{
Thread.Sleep(TimeSpan.FromSeconds(10));
try
{
if (LogUpdated != null)
update();
}
catch (Exception ex)
{
}
}
});
renewalThread.Start();
}
Are there more elegant ways to do this, thinking about the new async/await stuff?
What are the main differences to a solution doing something like
Task.run( () =>
{
await Task.delay(10000);
update code
}, __.LongRunning);
Use a Timer instead:
aTimer = new System.Timers.Timer(10000);
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Enabled = true;
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
// do something here.
// if this method could take longer than the intervale, disable the
// timer at the start and re-enable at the end.
}
With Timer you don't have to start a new thread. Thread.Sleep forces you to use a thread that sits and waits. If you want to do something every x seconds, that's what System.Threading.Timer is designed for, it will take a thread-pool thread and use that when calling the event and the thread will only be in use during the event--unlike Sleep. Sleep is inaccurate--it could be less than the time you asked for or more. the likelihood of it being that much off with 10 seconds is nil; but it's sill inaccurate. Using thread.Sleep means you can't do two events at once--if your Timer event handler took more time than the interval, it would run two handlers at a time. A Timer is much easier to stop--you just call Stop or Dispose. With Thread.Sleep you have to use Thread.Abort--and risk data corruption (i.e. you have to write the code that calls Thread.Sleep in such a way that cancelling the thread doesn't corrupt data). If you need to do something on the UI thread in the event, use Forms.Timer and you don't have to deal with marshalling back to the UI thread (e.g. Control.BeginInvoke).
I could go on, but I think you get the point. For more details, see http://bit.ly/IhxHSk