Cancelling Thread Timer from another class - c#

I'm attempting to implement the MSDN example (http://msdn.microsoft.com/en-us/library/swx5easy.aspx) for Thread.Timers in my own code.
I want to be able to cancel the timer when a certain user action is performed, however I can not dispose the timer, I suspect this is because I'm calling a method from another class so I need to adjust; but I don't know where.
Other than this, the timer works fine. Can anyone see why my timer will not cancel when btnconfigOpenConfig is called?
FYI I'm converting what was a worker process to a timed event.
public partial class Xservt : Window
{
internal class TimerStateObjClass
{
public int SomeValue;
public System.Threading.Timer SqlUpdateFromTwitterTimerReference;
public bool TimerCanceled;
}
internal void SomeMethod(){
TimerStateObjClass stateObj = new TimerStateObjClass();
stateObj.TimerCanceled = false;
stateObj.SomeValue = 100;
System.Threading.TimerCallback timerDelegate =
new System.Threading.TimerCallback(twit.hometimelineclass._sqlUpdateFromTwitterWorker_DoWork);
var sqlUpdateFromTwitterTimer = new Timer(timerDelegate, stateObj, 0,20000);
stateObj.SqlUpdateFromTwitterTimerReference = sqlUpdateFromTwitterTimer;
}
}
//action to perform which disposes the timer
private void btnconfigOpenConfig(object sender, RoutedEventArgs e)
{
TimerStateObjClass timerState = new TimerStateObjClass();
timerState.TimerCanceled = true;
}
//Actions the timer is calling, in another class
internal static void _sqlUpdateFromTwitterWorker_DoWork(object StateObj)
{
Xservt.TimerStateObjClass state = (Xservt.TimerStateObjClass) StateObj;
if(state.TimerCanceled)
{
state.SqlUpdateFromTwitterTimerReference.Dispose();
}
//some work
}

As Hans pointed out in the comments, you need to keep a reference to TimerStateObjClass you originally created. You can then use that to set TimerCanceled.
public partial class Xservt : Window
{
internal class TimerStateObjClass
{
public int SomeValue;
public System.Threading.Timer SqlUpdateFromTwitterTimerReference;
public bool TimerCanceled;
}
TimerStateObjClass stateObj; //THIS IS THE ORIGINAL STATE OBJ
internal void SomeMethod()
{
stateObj = new TimerStateObjClass();
stateObj.TimerCanceled = false;
stateObj.SomeValue = 100;
System.Threading.TimerCallback timerDelegate = new System.Threading.TimerCallback(twit.hometimelineclass._sqlUpdateFromTwitterWorker_DoWork);
var sqlUpdateFromTwitterTimer = new Timer(timerDelegate, stateObj, 0, 20000);
stateObj.SqlUpdateFromTwitterTimerReference = sqlUpdateFromTwitterTimer;
}
//action to perform which disposes the timer
private void btnconfigOpenConfig(object sender, RoutedEventArgs e)
{
//HERE WE CAN GET AT THE ORIGINAL STATE OBJ
stateObj.TimerCanceled = true;
}
}
//Actions the timer is calling, in another class
internal static void _sqlUpdateFromTwitterWorker_DoWork(object StateObj)
{
Xservt.TimerStateObjClass state = (Xservt.TimerStateObjClass)StateObj;
if (state.TimerCanceled)
{
state.SqlUpdateFromTwitterTimerReference.Dispose();
}
//some work
}

You need to store reference to your timer (or class that references the timer) somewhere in your class.
To stop the timer there is not need to dispose it. You can just call timer.Change(-1, -1);. That will allow to re-enable timer again by calling timer.Change(dueTimeInMs, intervalInMs);
You code should be something like that:
public partial class Xservt : Window
{
private Timer timer = new Timer(o => DoSomething());
private void StartTimer()
{
var period = 5 * 1000; // 5 sec
timer.Change(0, period);
}
private void StopTimer()
{
timer.Change(-1, -1);
}
}
Then call StartTimer to run it and StopTimer to stop respectively.
Also note that if there is any chance that DoSomething will run longer than timer interval that would result in running that method in more than one thread concurrently. To avoid that DO NOT use Timer's interval but use dueTime instead:
private Timer timer = new Timer(o => {
DoSomething();
StartTimer();
});
private void StartTimer()
{
var period = 5 * 1000; // 5 sec
timer.Change(period, 0);
}
In this timer is trigrered to run only once but after each run it gets re-triggered.

Related

How to keep a System.Timers.Timer stopped during ElapsedEvent

I am using System.Timers.Timer and every x seconds I need to perform some tasks in an ElapsedEvent method. While I am performing my tasks in the ElapsedEvent method, I want the timer to be stopped. However, I have another method that can start the timer, which can be called while the ElapsedEvent is running. My code looks something like this:
class MyClass {
Timer myTimer;
public MyClass {
myTimer = new System.Timers.Timer();
// init timer code here...
}
public void ElapsedEventTask(object source, ElapsedEventArgs e) {
myTimer.Enabled = false;
try
{
// do my tasks
}
catch
{
...
}
finally
{
myTimer.Enabled = true;
}
}
}
public void AnotherMethod() {
// do some things
myTimer.Enabled = true;
}
How do I prevent AnotherMethod from starting the timer while I'm completing the task in ElapsedEventTask?
You can add a variable that indicate if the task is running. Finaly to be thread safe, you need to use lock when this variable is used in with myTimer.Enabled :
class MyClass
{
object syncEnableRunning = new object();
bool running
Timer myTimer;
public void ElapsedEventTask(object source, ElapsedEventArgs e)
{
lock(syncEnableRunning)
{
running = true;
myTimer.Enabled = false;
}
try { /*do my tasks*/}
catch { ... }
finally
{
lock(syncEnableRunning)
{
myTimer.Enabled = true;
running = false;
}
}
}
public void AnotherMethod()
{
// do some things
lock(syncEnableRunning)
{
if(!running)
{
myTimer.Enabled = true;
}
}
}
}
According to the documentation the System.Timers.Timer class is not thread-safe, so it's not safe to touch its Enabled property from multiple threads without synchronization (doing so results to undefined behavior). Vernou's answer shows how to synchronize the threads by using locks, but personally I am a bit nervous with trying to enforce a non-overlapping execution policy using a mechanism that apparently was designed to be re-entrant. So my suggestion is to ditch the System.Timers.Timer, and use instead an asynchronous loop, controlled by Stephen Cleary's PauseTokenSource mechanism:
class MyClass
{
private readonly CancellationTokenSource _cts;
private readonly PauseTokenSource _pts;
public Task Completion { get; private set; }
public MyClass(TimeSpan interval)
{
_cts = new CancellationTokenSource();
_pts = new PauseTokenSource();
_pts.IsPaused = true;
Completion = Task.Run(async () =>
{
try
{
while (true)
{
await _pts.Token.WaitWhilePausedAsync(_cts.Token);
var delayTask = Task.Delay(interval, _cts.Token);
/* Do my tasks */
await delayTask;
}
}
catch (OperationCanceledException)
when (_cts.IsCancellationRequested) { } // Ignore
});
}
public void Start() => _pts.IsPaused = false;
public void Stop() => _pts.IsPaused = true;
public void Complete() => _cts.Cancel();
}
The PauseTokenSource is the controller of a PauseToken, a similar concept with the CancellationTokenSource/CancellationToken combo. The difference is that the CancellationTokenSource can be canceled only once, while the PauseTokenSource can be paused/unpaused multiple times. This class is included in the AsyncEx.Coordination package.
The MyClass exposes a Complete method that terminates the asynchronous loop, and a Completion property that can be awaited. It is a good idea to await this property before closing the program, to give to any active operation the chance to complete. Otherwise the process may be killed in the middle of a background execution, with unpredictable consequences.
I would create a one shot timer, which you then need to start again at the end of your timer function.
myTimer = new System.Timers.Timer();
myTimer.AutoReset = false;
public void ElapsedEventTask(object source, ElapsedEventArgs e) {
...
finally
{
myTimer.Start();
}
}

Timer not starting unless function hasn't been called for certain amount of time

When the command .checked is not done for 15 minutes, I would like a timer to spam a message every minute. Right now I have it set to some ridiculously fast amount for testing purposes. Check() is called when .checked is done.
I tried doing something like this:
public static void Check()
{
MinecraftClient.ChatBots.DiscordWallTimer.TimerOn = false;
Program.StartTimer();
}
public static System.Timers.Timer EnableTimer;
public static Task StartTimer()
{
EnableTimer = new Timer()
{
Interval = 15 * 1000,
AutoReset = false,
Enabled = true
};
EnableTimer.Elapsed += OnTimerTicked;
return Task.CompletedTask;
}
public static void OnTimerTicked(object sender, ElapsedEventArgs e)
{
MinecraftClient.ChatBots.DiscordWallTimer.TimerOn = true;
MinecraftClient.ChatBots.DiscordWallTimer.StartTimer();
}
And having this in my timer class:
public class DiscordWallTimer
{
public static bool TimerOn;
public static System.Timers.Timer wallTimer;
internal static Task StartTimer()
{
Console.WriteLine("Wall timer has started");
wallTimer = new Timer()
{
Interval = 5*1000,
AutoReset = true,
Enabled = TimerOn
};
wallTimer.Elapsed += OnTimerTicked;
return Task.CompletedTask;
}
private static void OnTimerTicked(object sender, ElapsedEventArgs e)
{
if (TimerOn == true)
{
Program.SendAlertDiscord();
}
}
}
Only problem is that the alert still sends if .check has been done recently (I think for testing I set it to 15 seconds.)
Thanks in advance!
I'd suggest using a library designed to do this kind of thing rather than mucking around with timers - which can be hard.
Try Microsoft's Reactive Framework:
private static Subject<Unit> _check = new Subject<Unit>();
private static IDisposable _subscription = null;
private static void SetUp()
{
_subscription =
_check
.Select(x => Observable.Timer(TimeSpan.FromMinutes(15.0), TimeSpan.FromMinutes(1.0)))
.Switch()
.Subscribe(x => Program.SendAlertDiscord());
}
public static void Check()
{
_check.OnNext(Unit.Default);
}
That's it. Just call SetUp once and then whenever you call Check() you'll start a 15 minute timer that then spams every minute. Any call to Check() will reset the timer automatically.
And call _subscription.Dispose(); if you want to shut down the code.
Just NuGet "System.Reactive" to get the bits and then add using System.Reactive.Linq; to your code.
If you have any threading issues let me know any I'll help get the code to marshall to the right thread for you.
in fact you dont stop the timer DiscordWallTimer, you just do some modifications and all will be ok and
You dont need the variable TimerOn
public static void Check()
{
// the first time walltimer doesnt exist
if (MinecraftClient.ChatBots.DiscordWallTimer.wallTimer != null)
{
MinecraftClient.ChatBots.DiscordWallTimer.wallTimer.Stop();
}
Program.StartTimer();
}
public class DiscordWallTimer
{
public static System.Timers.Timer wallTimer;
internal static Task StartTimer()
{
Console.WriteLine("Wall timer has started");
wallTimer = new Timer()
{
Interval = 5*1000,
AutoReset = true,
Enabled = true // <- keep True
};
wallTimer.Elapsed += OnTimerTicked;
return Task.CompletedTask;
}
private static void OnTimerTicked(object sender, ElapsedEventArgs e)
{
Program.SendAlertDiscord();
}
}

C# wait timeout before calling method and reset timer on consecutive calls

I have a event in my code that can possibly get fired multiple times a second at some moment.
However I would like to implement a way to make that method wait 500ms before really firing, if the method gets called again before those 500ms are over, reset the timer and wait for 500ms again.
Coming from javascript I know this is possible with setTimeout or setInterval. However I'm having trouble figuring out how I could implement such a thing in C#.
You could use a System.Timers.Timer wrapped in a class to get the behaviour you need:
public class DelayedMethodCaller
{
int _delay;
Timer _timer = new Timer();
public DelayedMethodCaller(int delay)
{
_delay = delay;
}
public void CallMethod(Action action)
{
if (!_timer.Enabled)
{
_timer = new Timer(_delay)
{
AutoReset = false
};
_timer.Elapsed += (object sender, ElapsedEventArgs e) =>
{
action();
};
_timer.Start();
}
else
{
_timer.Stop();
_timer.Start();
}
}
}
This can then be used in the following manner:
public class Program
{
static void HelloWorld(int i)
{
Console.WriteLine("Hello World! " + i);
}
public static void Main(string[] args)
{
DelayedMethodCaller methodCaller = new DelayedMethodCaller(500);
methodCaller.CallMethod(() => HelloWorld(123));
methodCaller.CallMethod(() => HelloWorld(123));
while (true)
;
}
}
If you run the example, you will note that "Hello World! 123" is only displayed once - the second call simply resets the timer.
If you need to reset the timer when the method is called again, consider looking at the ManualResetEvent class:
https://msdn.microsoft.com/en-us/library/system.threading.manualresetevent(v=vs.110).aspx
You can use this to notify one or more waiting threads that an event has occurred.
You can use Thread.Sleep() with locking
private object locking = new object();
lock (locking )
{
Thread.Sleep(500);
//Your code to run here
}
https://msdn.microsoft.com/en-us/library/system.threading.thread.sleep(v=vs.110).aspx
Just writen super simple class with System.Threading.Thread; With a little different approach Usage.
var delayedCaller = new DelayedTimeout(() => HelloWorld(123), 500, false);
delayedCaller.ResetTimer();
delayedCaller.ResetTimer();
Currently, you can do it very simple with the following class
public class DelayedTimeout
{
readonly Timer _timer;
readonly int _timeoutMs;
public DelayedTimeout(TimerCallback callback, int timeoutMs, bool startNow)
{
_timeoutMs = timeoutMs;
// Should we start now
var currentTimeoutMs = startNow ? _timeoutMs : Timeout.Infinite;
_timer = new Timer(callback, null, currentTimeoutMs, Timeout.Infinite);
}
// Constructor overloading
public DelayedTimeout(Action callback, int timeoutMs, bool startNow) :
this(delegate (object? obj) { callback.Invoke(); }, timeoutMs, startNow)
{}
public void ResetTimer()
{
_timer.Change(Timeout.Infinite, Timeout.Infinite); // Stop the timer
_timer.Change(_timeoutMs, Timeout.Infinite); // Stop the timer
}
}

Why does my use of System.Threading.Timer not work?

I'm trying to execute a function periodically, using a System.Threading.Timer. It calls the function, but it doesn't work, and doesn't report an error. Why?
public class Timerr
{
ArrayList listurl;
ArrayList listcategory;
protected Collection<Rss.Items> list = new Collection<Rss.Items>();
RssManager reader = new RssManager();
System.Threading.Timer Timer;
System.DateTime StopTime;
public void Run()
{
StopTime = System.DateTime.Now.AddMinutes(1);
Timer = new System.Threading.Timer(TimerCallback, null, 0,1000);
}
private void TimerCallback(object state)
{
if (System.DateTime.Now >= StopTime)
{
Timer.Dispose();
return;
}
callrss();
}
}
This works in LINQPad:
void Main()
{
var t = new Timerr();
t.Run();
Thread.Sleep(60000);
}
public class Timerr
{
System.Threading.Timer Timer;
System.DateTime StopTime;
public void Run()
{
StopTime = System.DateTime.Now.AddMinutes(1);
Timer = new System.Threading.Timer(TimerCallback, null, 0,1000);
}
private void TimerCallback(object state)
{
if (System.DateTime.Now >= StopTime)
{
Timer.Dispose();
return;
}
Console.WriteLine("Hello");
}
}
I recommend you install the free LINQPad with which you can check such things very quickly, without the need to run your entire application
Did you construct an instance of Timerr? Did you call Run on that instance? Did you keep that instance around so that the timer isn't GCed (System.Threading.Timers aren't automatically rooted, like System.Timers.Timers are)? Do you have some busy loop or some other way of keeping your process alive long enough to allow the timer callback to be invoked?

Call Method B if method A is not called for more than N seconds

I'm using following code to call Method B after N seconds method A is called. If method A
is called again within the N seconds timeout, i have to reset the time counting back to N seconds.
I cannot reference System.Windows.Form in my project, so I cannot use System.Windows.Form.Timer.
The method B must be called in the same thread A is called.
private void InitTimer()
{
timer = new BackgroundWorker();
timer.WorkerSupportsCancellation = true;
timer.WorkerReportsProgress = true;
timer.DoWork += delegate(object sender, DoWorkEventArgs e)
{
var st = DateTime.Now;
while (DateTime.Now.Subtract(st).TotalSeconds < 10)
{
if (timer.CancellationPending)
{
e.Cancel = true;
return;
}
}
};
timer.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
{
if (!e.Cancelled)
{
MethodB();
}
else
{
timer.RunWorkerAsync();
}
};
}
public void MethodA()
{
if (timer.IsBusy)
timer.CancelAsync();
else
timer.RunWorkerAsync();
}
public void MethodB()
{
//do some stuff
}
Actually the code work, but i think it's a bit confounding. Do you know if there is a best practices to achieve the same result?
It's a shame you're stuck on .NET 2.0, because Rx extensions has a Throttle method that achieves this effect quite elegantly.
Sadly Rx requires at least .NET 3.5 SP1.
Oh well! You can always use a System.Threading.Timer to get this done instead. Synchronization can be provided by leveraging the current SynchronizationContext (this is what BackgroundWorker does).
Here's a sketch of a LaggedMethodPair class to illustrate this approach. The class takes three inputs in its constructor: an Action to be performed on-demand, another Action to serve as the callback that will be invoked when a given timeout has elapsed, and, of course, the timeout itself:
public sealed class LaggedMethodPair
{
private SynchronizationContext _context;
private Timer _timer;
private Action _primaryAction;
private Action _laggedCallback;
private int _millisecondsLag;
public LaggedMethodPair(Action primaryAction,
Action laggedCallback,
int millisecondsLag)
{
if (millisecondsLag < 0)
{
throw new ArgumentOutOfRangeException("Lag cannot be negative.");
}
// Do nothing by default.
_primaryAction = primaryAction ?? new Action(() => { });
// Do nothing by default.
_laggedCallback = laggedCallback ?? new Action(() => { });
_millisecondsLag = millisecondsLag;
_timer = new Timer(state => RunTimer());
}
public void Invoke()
{
// Technically there is a race condition here.
// It could be addressed, but in practice it will
// generally not matter as long as Invoke is always
// being called from the same SynchronizationContext.
if (SynchronizationContext.Current == null)
{
SynchronizationContext.SetSynchronizationContext(
new SynchronizationContext()
);
}
_context = SynchronizationContext.Current;
ResetTimer();
_primaryAction();
}
void ResetTimer()
{
_timer.Change(_millisecondsLag, Timeout.Infinite);
}
void RunTimer()
{
_context.Post(state => _laggedCallback(), null);
}
}
I wrote a sample Windows Forms app to show this class in action. The form contains a LaggedMethodPair member with a timeout of 2000 ms. Its primaryAction adds an item to a list view. Its laggedCallback adds a highlighted item to the list view.
You can see that the code runs as expected.
I would encapsulate this functionality into a timer class with events that other classes can subscribe to (for example a timer.tick event).
I am trying to use AutoResetEvent, because it is capable to wait for a signal. I use it to have worker waited for the signal from A(), and if it has been too long B() will be called.
class Caller
{
AutoResetEvent ev = new AutoResetEvent(false);
public void A()
{
ev.Set();
// do your stuff
Console.Out.WriteLine("A---");
}
void B()
{
Console.Out.WriteLine("B---");
}
public void Start()
{
var checker = new BackgroundWorker();
checker.DoWork += new DoWorkEventHandler(checker_DoWork);
checker.RunWorkerAsync();
}
void checker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (!worker.CancellationPending)
{
bool called = ev.WaitOne(TimeSpan.FromSeconds(3));
if (!called) B();
}
}
}
I have tested my class roughly and it is working fine so far. Note that B will be called from worker thread, so you have to do the synchronization in B() if needed.

Categories

Resources