问题
I'm currently stuck on a problem with a timer.
I've got a function that is started by a timer every x seconds. Now the function execution can sometimes take longer under different conditions. So I want the timer to rerun only when it has completed the function. How can I achieve this?
回答1:
Right before it exits, your function could trigger an event. The event will be caught by another function, which will start the timer.
回答2:
My advice would be to avoid using a Timer class for this. The reason for this is precisely the problems you have with dealing this now.
The System.Threading.Timer class is re-entrant, which means that if the interval elapses before the previous trigger call returns, it will call the method again even while it is already executing. You need to handle cooperation between the method and the trigger in a way that makes the code a bit messy.
However, let's rethink the problem.
You want to
- Call a method on an interval
- If a method call takes longer to execute than the interval, immediately trigger again
A better way would be to dispense with the trigger and write your own using tasks.
Here's a very simple example:
public static async Task CallPeriodicAsync(Func<CancellationToken, Task> func,
int intervalMilliseconds, CancellationToken cancellationToken)
{
while (true)
{
var delay = Task.Delay(intervalMilliseconds, cancellationToken);
await func(cancellationToken);
await delay;
}
}
You would start it like this:
CancellationToken cancellationToken = ...;
CallPeriodicAsync(async ct =>
{
int timeToRun = 1000 + r.Next(14000);
Console.WriteLine($"This time running for {timeToRun} ms");
await Task.Delay(timeToRun, ct);
}, 10000, cancellationToken);
Sample output would be (I also had some Console.WriteLine in the CallPeriod method to say whether it will wait or immediately trigger in this run):
This time running for 12162 ms
trigger again immediately
This time running for 14706 ms
trigger again immediately
This time running for 12756 ms
trigger again immediately
This time running for 2187 ms
delay until next is 7813 ms
This time running for 5221 ms
delay until next is 4767 ms
This time running for 8866 ms
Basically this CallPeriod method will call your method and give it X milliseconds to complete. If it completes faster it will add a delay for the remainder, if it completes slower it will just restart the cycle.
This means that my question about "keeping the original interval" is still relevant.
For instance, keeping the original interval would result in this kind of triggering (---*
is just the timeline), every 4 seconds:
v v v v v
*---*---*---*---*---*---*---*---*---*---*---*---*---*---*---*---*---*
[...] [..................][.......] [...] [....
whereas my solution above would skew the cycle after a delay:
v v v v v
*---*---*---*---*---*---*---*---*---*---*---*---*---*---*---*---*---*
[...] [..................][.......] [...] [....
v ^
+-- because we got -+
one second delayed
here we're now one
second late always
来源:https://stackoverflow.com/questions/62001972/timer-to-restart-function-when-completed