I'm just starting in C# and this is my first question so apologies if this is dumb or the wrong way to ask..
I have a list of timers:
private List<MyTimerClass> MyTimerClassList = new List<MyTimerClass>();
where MyTimerClass just contains a value to keep track of the ID of this timer and a method to initialize it:
timer = new System.Timers.Timer();
and I'm setting up these timers in a standard way with an ElapsedEventHandler:
foreach (var p in AnotherList)
{
var t = new MyTimerClass();
t.init_timer();
t.id = MyOtherClass.ID;
t.timer.Interval = p.Interval;
t.timer.Elapsed += new ElapsedEventHandler((source, e) => RunMyTimerEvent(source, e, p));
t.timer.Enabled = true;
MyTimerClassList.Add(t);
};
These timers are meant to keep running indefinitely and the event handlers run some asynchronous web stuff. Sometimes, the program may get some info that requires it to reschedule, add or remove any or all of these timers, so I have another timer event that regularly checks an update schedule and re configures any of the above timers if needed:
// first check to see see if a timer should be removed
foreach (var t in MyTimerClassList)
{
if (!(UpdatedListofTimers.Exists(p => p.ID == t.id)))
{
t.timer.Stop();
t.timer.Dispose();
}
}
// then check to see if any of the timer intervals has been changed
foreach (var t in UpdatedAnotherList)
{
var item = MyTimerClassList.Find(p => p.id == MyTimerClass.ID);
if (item != null)
{
if (item.timer.Interval != (t.interval * 1000))
{
item.timer.Stop();
item.timer.Interval = (t.Interval * 1000);
item.timer.Start();
}
}
// leaves us with adding a new timer
else {
var n = new MyTimerClass();
n.init_timer();
n.id = t.ID;
n.timer.Interval = t.interval * 1000;
n.timer.Elapsed += new ElapsedEventHandler((sender, ev) => RunMyTimerEvent(sender, ev, t));
n.timer.Enabled = true;
MyTimerClassList.Add(n);
}
};
The code works; it works fine, timers are updated and rescheduled and reconfigured as they should be, they fire off the events and do their stuff. The timer handler event is basically a call to a method that starts off a task:
public void RunMyTimerEvent(object source, ElapsedEventArgs e, AnotherList x)
{
Task<int> t = Task.Run(() => AsyncProcessClass.SomeAsyncStuff(x));
t.Wait();
t.Dispose();
}
The problem is that there is huge memory leak, memory use ramps up very quickly and doesn't get garbage collected even with a hard call to GC.Collect. Inside the async process that I'm calling, there aren't any memory leaks or handlers that aren't disposed of (and I'm aware that you don't necessarily have to dispose of stuff, the garbage collector is smart enough to know whats no longer referenced).
If, instead of reconfiguring the timers when I get an update, I delete them all and recreate them from scratch, the memory leak goes away. Am I doing something completely wrong here ? Instead of changing the timer interval on the fly as it were, do you have to delete it and create a new one ? Can another timer event not reconfigure a different timer ? Is it the weak reference problem with the method inside the event handler, that it never gets marked for garbage collection ? I don't understand weak references that well, at least not enough to have tried it. Maybe I'm doing something that's obviously wrong ?
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();
}
Basically, I have a thread that downloads and reports the download status to a progress bar and a label. It always crashes when trying to invoke a object in a disposed form (Progress bar & label) even when there's a if (!this.Disposed) is called before, and still throws the exception even with a catch (ObjectDisposedException) is called in the same try block. I'm not sure what I can do to fix this, it's probably best described as the most annoying thing I've ever encountered.
Thanks you.
Update (from a considerate SO lurker) of my source found on pastebin
Thread downloader();
public bool abortThread = false();
private void frmDownload_FormClosing(object sender, FormClosingEventArgs e) {
downloader.Abort(); // Abort the thread before closing the form...?
abortThread = true; // Set the abortThread to true
this.Dispose(); // Dispose thread
}
downloader = new Thread(() => {
string[] URLs = { "http://test1.com/", "http://test2.com/", "http://test3.com/" };
try {
using (WebClient wc = new WebClient()) {
wc.DownloadProgressChanged += (s, e) => {
if (!pbDownloadStatus.IsDisposed && !lbPercentage.IsDisposed) {
if (!abortThread) {
this.Invoke((MethodInvoker)(() => pbDownloadStatus.Value = e.ProgressPercentage)); // EXCEPTION HAPPENS HERE
this.Invoke((MethodInvoker)(() => pbDownloadStatus.Value++));
this.Invoke((MethodInvoker)(() => pbDownloadStatus.Value--));
this.Invoke((MethodInvoker)(() => lbPercentage.Text = e.ProgressPercentage.ToString() + "%"));
}
}
};
wc.DownloadFileCompleted += (s, e) => {
if (!pbDownloadStatus.IsDisposed && !lbPercentage.IsDisposed) {
lock (e.UserState) {
this.Invoke((MethodInvoker)(() => pbDownloadStatus.Value = 0));
this.Invoke((MethodInvoker)(() => lbPercentage.Text = "0%"));
Monitor.Pulse(e.UserState);
}
}
};
wc.Proxy = WebProxy.GetDefaultProxy();
wc.Headers.Add(header);
for (int i = 0; i < URLs.Length; i++) {
var sync = new Object();
lock (sync) {
wc.DownloadFileAsycn(new Uri(URLs[i]), "C:\Test\URL" + i);
Monitor.Wait(sync);
}
}
}
}
}
catch (ObjectDisposedException disEx) { // Never gets caught
downloader.Abort();
MessageBox.Show("Object was disposed");
}
});
downloader.Start();
From Msdn
Beginning with the .NET Framework 4, multithreaded programming is
greatly simplified with the System.Threading.Tasks.Parallel and
System.Threading.Tasks.Task classes, Parallel LINQ (PLINQ), new
concurrent collection classes in the System.Collections.Concurrent
namespace, and a new programming model that is based on the concept of
tasks rather than threads
The need to work with threads directly in the modern era is greatly reduced, and you should probably look at Tasks and async/await Tasks can be cancelled, are easier to manage and async returns back to the calling context.
Secondly, your code doesn't make sense, and its full of compiler errors which is not a good start for a question. Additionally, since there is so much wrong with the code i have decided to just give you lots of points to think about apposed to rewriting it all
So lets look at some of the more obvious issues this code has.
abortThread is accessed from multiple threads and is not thread safe for the most part.
You are calling var sync = new Object(); directly before lock (sync) meaning you are locking nothing
Even if that lock statement was going to work, there is no other thread using the lock, meaning its redundant.
You are calling this.Dispose() from the forms closing event. This is unusal to say the least
The two conditions when a form is not disposed on Close is when (1) it
is part of a multiple-document interface (MDI) application, and the
form is not visible; and (2) you have displayed the form using
ShowDialog. In these cases, you will need to call Dispose manually to
mark all of the form's controls for garbage collection.
Basically in a non MDI application, If you call ShowDialog then put it in a using statement.
You are trying to check IsDisposed to determine if its safe to marshal back to the UI thread.
Just because you call Dispose doesn't mean the form IS disposed, this is not how it works and will not solve your problem.
If you need to do asynchronous IO bound work, Use the aysnc,await pattern, then you wont be blocking a thread for spurious reasons waiting for a completion port. If you need to run this in parallel, then consider DataFlow with action blocks so you can take advantage of aysnc,await and parallel.
If you need to determine whether a form is alive or dead, use a thread safe variable. Use a static Lock object, and every where you update the variable use lock as well.
if you need to marshal to the UI thread. Don't do this.Invoke((MethodInvoker)(() multiple times, Do it once, and update everything at once.
So I'm working on a Task Scheduling program using a System.Timers.Timer object to wait between tasks. For using this timer I'm throwing it into a static (extension) method on instantiation and using it only there. My question is will it be properly disposed of at the conclusion of that static method or do I need to do something more? Here's what I'm working with.
public static void Use<T>(this T o, Action<object> work) => work(o);
private void RunLoop()
{
while (!Stop)
{
try
{
//Method 1
new System.Timers.Timer((task.NextRun - DateTime.Now).TotalMilliseconds).Use(timer => {
timer.Elapsed += (s, e) => {
new Thread(new ThreadStart(RunProc)) {
Name = task.Title,
IsBackground = true
}.Start();
};
timer.AutoReset = false;
timer.Start();
});
//Method 2
using (var timer = new System.Timers.Timer((task.NextRun - DateTime.Now).TotalMilliseconds))
{
timer.Elapsed += (s, e) =>
{
new Thread(new ThreadStart(RunProc))
{
Name = task.Title,
IsBackground = true
}.Start();
};
timer.AutoReset = false;
timer.Start();
}
} catch(SqlException ex)
{ //Omitted for size }
catch (Exception ex)
{ //Omitted for size }
}
}
Will method 1 properly dispose of my timer, or do I have to call dispose or deal with a using statement for GC to get it? From what I've been reading for most objects they will get GC'ed when they fall out of scope of that lambda call in Use. But apparently Timers are different in that they connect to some un-managed resources which can cause them to persist even when you would expect they'd get cleaned up, like say going out of scope.
Although it's not technically required, Timers should always be disposed properly (just like nearly everything that inherits from IDisposable). What will happen if you don't dispose the timers? They will build up until a garbage collection detects that they're no longer reachable and then they will be finalized (which should automatically do the same thing as disposing of them, but it isn't as efficient).
In your method 2, the timer will likely not ever fire because it will be disposed immediately after it is created (before the Elapsed delegate runs).
So, the question becomes, when should you dispose of the timer? Well, that depends on how it is used. If you're only using it to execute that little code block once, you can dispose of it at the end of that execution. If it's supposed to trigger more than once, you'll need to have some other outside owner of the timer that disposes of it when it is no longer needed.
For closure, I ended up creating a list of timers for each action, and then just update the intervals, and recycle them as needed. Then added a specific function to properly stop and dispose of all of them, when stopping the service. The setup I have above fails once there are 2 tasks scheduled for the same time, so it wouldn't have worked either way. With this new way, I don't have to worry about it as they're either recycled indefinitely or specifically disposed of.
The following code is called from a Task t = new task(() => StartScanAsync(path));
but the timer Event is not firing, the Timer is started in StartTimer.
private void StartScanAsync(string path)
{
StartTimer();
foreach (string fsPath in Alphaleonis.Win32.Filesystem.Directory.EnumerateFileSystemEntries(path, "*", SearchOption.AllDirectories, true))
{
Alphaleonis.Win32.Filesystem.FileInfo finfo = new Alphaleonis.Win32.Filesystem.FileInfo(fsPath);
fileClass.AddFile(finfo);
// UpdateStatus(fsPath);
UpdateTotalFilesScanned();
TotalFileSize += finfo.Length;
UpdateTotalFileSize(TotalFileSize);
IdentifyFileType.FileType ft = FileType.LoadPathRet(fsPath);
// Application.DoEvents();
}
UpdateStatus("Done Scanning.");
StopTimer();
}
I tried a Parrallel.ForEach as well and have the same problem.
The following code is supposed to start a timer, run the Parrallel.ForEach over a large collection and then stop the Timer. However, the Timer event is not being Fired... Any ideas why and how to solve this?
StartTimer();
Parallel.ForEach(Alphaleonis.Win32.Filesystem.Directory.EnumerateFileSystemEntries(path, "*", SearchOption.AllDirectories), fsPath =>
{
Alphaleonis.Win32.Filesystem.FileInfo finfo = new Alphaleonis.Win32.Filesystem.FileInfo(fsPath);
fileClass.AddFile(finfo);
UpdateStatus(fsPath);
UpdateTotalFilesScanned();
TotalFileSize += finfo.Length;
UpdateTotalFileSize(TotalFileSize);
// IdentifyFileType.FileType ft = FileType.LoadPathRet(fsPath);
Application.DoEvents();
});
StopTimer();
The First code works when not in a Task, and the second code is not run in a Task but still does not work.
So To Clarify:
Question 1) Why does the timer (System.Windows.Forms.Timer (I think)) Not fire in a Task, and also does not fire when surrounding a Parallel.ForEach?
Question 2) How can I raise a Tick every second (to get Files Scanned Per Second) within the Parallel.ForEach task and the Task<> task?
.NET offers quite a few different timers. You can find a detailed list and explanation in this article about Comparing the Timer Classes in the .NET Framework Class Library.
The problem is that you are using a System.Windows.Forms.Timer which should only be used on the UI thread. In a test scenario using this timer won't work even for such a simple case:
Task.Factory.StartNew(() =>
{
var timer = new Timer { Enabled = true, Interval = 1000 };
timer.Tick += (s, e) =>
Console.WriteLine("This will never be written to the console!");
});
I'm assuming that you create the timer or modify its properties in the StartTimer or StopTimer methods. Even if you create the timer on the UI thread you actually have to set Interval and Enabled on the same UI thread too.
Since you are using the timers in different tasks (by using a Task or Parallel.For) you have to use a different timer, e.g. the System.Threading.Timer.
Your second case could be same, but it's hard to tell without knowing the code that calls the code snippet with the parallel loop.
Lets say I have a button that gets clicked and it does this:
public void ButtonClick(object sender, EventArgs e)
{
System.Timers.Timer NewTimer = new System.Timers.Timer();
NewTimer.AutoReset = false;
NewTimer.Elapsed += new ElapsedEventHandler(TimerElapsed);
NewTimer.Interval = 1000;
NewTimer.Start();
}
public void TimerElapsed(object sender, ElapsedEventArgs e)
{
}
If this button gets clicked 100 times what happens to those instances that have been created? Will garbage collection kick in or does the System.Timers.Timer.Close method need calling and if it does where do you call it from?
No this will not cause a memory leak. In fact the way your code is written it's not guaranteed to execute properly. Timers.Timer is really just a wrapper over Threading.Timer and it's explicitly listed as being collectable even if it's currently running.
http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx
Here you keep no reference to it and hence the very next GC could collect it while your form is still running and before the event ever fires
EDIT
The documentation for Timers.Timer appears to be incorrect. The Timer instance will not be collected if it's unreferenced. It will indeed live on
var timer = new System.Timers.Timer
{
Interval = 400,
AutoReset = true
};
timer.Elapsed += (_, __) => Console.WriteLine("Stayin alive (2)...");
timer.Enabled = true;
WeakReference weakTimer = new WeakReference(timer);
timer = null;
for (int i = 0; i < 100; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine("Weak Reference: {0}", weakTimer.Target);
Console.ReadKey();
They will be collected once method is left. TimerElapsed will be either called or not depending on when Timer gets finalized. Most likely it will be dead long before 1 second passed.
When you call Timer.Close() you thus call Timer.Dispose() that de-registers timer from timer queue and in that case TimerElapsed won't be called (of course if it was not called before).
If you leave timer not closed, GC will eventaully call Finalize() that in turn will call Dispose(). But there is not exact knowledge when it will happen :)
See below example, Console.Out.WriteLine("called!!!") will never execute:
using (System.Timers.Timer NewTimer = new System.Timers.Timer())
{
NewTimer.AutoReset = false;
ElapsedEventHandler TimerElapsed = (sender, args) => { Console.Out.WriteLine("called!!!"); };
NewTimer.Elapsed += new ElapsedEventHandler(TimerElapsed);
NewTimer.Interval = 1000;
NewTimer.Start();
}
Thread.Sleep(3000);
After answers by the_joric and JaredPar and running profiler tests which showed timers sticking around after garbage collection kicked in the reason they stuck around was because there is a reference to the event handler sticking around. For a more detailed explanation see this answer.
The real answer is that it is a memory leak unless the timer is closed in the elapsed event handler.
Just goes to show that although I trust the answers on SO (maybe too much) from the great contributors they may be slightly off.