问题
I'm unsure how best to cancel a task that is running a system timer. In the code below, every 60 mins the timer will elapse and then run another method (CheckFileOverflow) that is used to check the file size of a system log txt. file
Cancellation of the timer ideally would be done by a button click or another method that calls the cancellation. The timer will effectively be allowed to run for as long as the software is running, but when the user eventually shuts down the software i'd like to be able to cancel the task in a responsible fashion i.e. not run the risk of ongoing thread pool resources lingering being used in the background.
I have spent many many hours reading up on cancellation tokens but still don't get it :(
public void SystemEventLoggerTimer()
{
SysEvntLogFileChckTimerRun = true;
Task.Run(() =>
{
System.Timers.Timer timer = new System.Timers.Timer
{ Interval = 1000 * 60 * 60 };
timer.Elapsed += new ElapsedEventHandler(CheckFileOverflow);
timer.Start();
});
}
回答1:
I'd suggest that you use Microsoft's Reactive Framework (aka Rx) - just NuGet System.Reactive
.
Then you do this:
IDisposable subscription =
Observable
.Interval(TimeSpan.FromHours(1.0))
.Subscribe(_ => CheckFileOverflow());
When you want to cancel the subscription just call subscription.Dispose()
.
Rx is ideal for abstracting away timers, events, tasks, async operations, etc.
回答2:
You can change your method to something like this
public void SystemEventLoggerTimer(CancellationToken cancelToken)
{
SysEvntLogFileChckTimerRun = true;
Task.Run(async () =>
{
// Keep this task alive until it is cancelled
while (!cancelToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromMinutes(60));
CheckFileOverflow();
}
});
}
Then you call SystemEventLoggerTimer like this
var cancelSource = new CancellationTokenSource();
SystemEventLoggerTimer(cancelSource.Token);
you can cancel this Token when program has been disposed or simply at the end of your main function
回答3:
Why not just have a timer accessible in the calling context (or globally in your class/application) - you'd have to do that with the CancellationTokenSource
anyway! This doesn't look like the right use case for a Task
.
Try this:
public void SystemEventLoggerTimer(System.Timers.Timer timer)
{
SysEvntLogFileChckTimerRun = true;
timer.Elapsed += new ElapsedEventHandler(CheckFileOverflow);
timer.Start();
}
Calling code:
var timer = new System.Timers.Timer() { Interval = 1000 * 60 * 60 };
SystemEventLoggerTimer(timer);
Cancellation code (in cancel button's event handler, etc):
timer.Stop();
回答4:
I have posted below what appears to be a satisfactory solution which worked for me. Hopefully I'm responding to the thread in the correct manner... (a newbie to stackOverflow) I setup a quick windows form for testing, I created 2qty buttons and 1qty textbox. Buttons are used to Start & Stop the timer (using cancellation token) The textbox is used to monitor the timer which will update with "Timer Running" message every 2 seconds. Hope this helps anyone else looking at a similar scenario...
enter image description here
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private CancellationTokenSource cancelSource;
// Button is used to START the timer.
private void TimerStartButton_Click(object sender, EventArgs e)
{
cancelSource = new CancellationTokenSource();
// Run the below method that will initiate timer to start running from
// the button click.
SystemEventLoggerTimer(cancelSource.Token);
}
private void SystemEventLoggerTimer(CancellationToken cancelToken)
{
Task.Run(async () =>
{
// Keep this task alive until it is cancelled
while (!cancelToken.IsCancellationRequested)
{
// Encapsulating the function Task.Delay with 'cancelToken'
// allows us to stop the Task.Delay during mid cycle.
// For testing purposes, have reduced the time interval to 2 secs.
await Task.Delay(TimeSpan.FromSeconds(2), cancelToken);
// Run the below method every 2 seconds.
CheckFileOverflow();
}
});
}
// When the below method runs every 2 secs, the UpdateUI will allow
// us to modify the textbox form controls from another thread.
private void CheckFileOverflow()
{
UpdateTextbox("Timer Running");
}
// UpdateUI will allow us to modify the textbox form controls from another thread.
private void UpdateTextbox(string s)
{
Func<int> del = delegate ()
{
textBox1.AppendText(s + Environment.NewLine);
return 0;
};
Invoke(del);
}
// Button that is used to STOP the timer running.
private void TimerStopButton_Click(object sender, EventArgs e)
{
// Initiate the cancelleation request to method "SystemEventLoggerTimer"
cancelSource.Cancel();
}
}
来源:https://stackoverflow.com/questions/54612247/c-sharp-task-cancellation-when-using-system-timers