@@ -9,16 +9,21 @@ public sealed class PeriodicTask : IPeriodic, IDisposable
99 {
1010 #region Private Properties
1111
12- private Task _task ;
12+ private PeriodicTimer _timer ;
13+
14+ private Task ? _task ;
1315
14- private Func < CancellationToken , Task > _action ;
16+ private readonly Func < CancellationToken , Task > _action ;
1517
1618 private CancellationTokenSource _stoppingCts ;
1719
1820 private int _interval = Timeout . Infinite ;
1921
2022 private int _startDelay = Timeout . Infinite ;
2123
24+ private bool _running = false ;
25+ private readonly object _runningLock = new object ( ) ;
26+
2227 #endregion
2328
2429
@@ -65,12 +70,7 @@ public int StartDelay
6570 /// <exception cref="System.ArgumentOutOfRangeException"></exception>
6671 public PeriodicTask ( Func < CancellationToken , Task > action , int interval , int startDelay = 0 )
6772 {
68- if ( action == null )
69- {
70- throw new ArgumentNullException ( nameof ( action ) ) ;
71- }
72-
73- _action = action ;
73+ _action = action ?? throw new ArgumentNullException ( nameof ( action ) ) ;
7474
7575 if ( interval < 0 )
7676 {
@@ -85,6 +85,10 @@ public PeriodicTask(Func<CancellationToken, Task> action, int interval, int star
8585 }
8686
8787 _startDelay = startDelay ;
88+
89+ _timer = new PeriodicTimer ( TimeSpan . FromMilliseconds ( _interval ) ) ;
90+
91+ _stoppingCts = new CancellationTokenSource ( ) ;
8892 }
8993
9094 #endregion
@@ -97,11 +101,23 @@ public PeriodicTask(Func<CancellationToken, Task> action, int interval, int star
97101 /// </summary>
98102 public Task Start ( )
99103 {
104+ lock ( _runningLock )
105+ {
106+ if ( _running == true )
107+ {
108+ return Task . CompletedTask ;
109+ }
110+
111+ _running = true ;
112+ }
113+
100114 if ( _task != null )
101115 {
102116 return Task . CompletedTask ;
103117 }
104118
119+ _timer = new PeriodicTimer ( TimeSpan . FromMilliseconds ( _interval ) ) ;
120+
105121 _stoppingCts = new CancellationTokenSource ( ) ;
106122
107123 try
@@ -118,27 +134,49 @@ public Task Start()
118134 /// <summary>
119135 /// Stop the <see cref="PeriodicTask"/>
120136 /// </summary>
121- public Task Stop ( )
137+ public async Task Stop ( )
122138 {
123- if ( _stoppingCts != null )
139+ lock ( _runningLock )
124140 {
125- _stoppingCts . Cancel ( ) ;
141+ if ( _running == false )
142+ {
143+ return ;
144+ }
145+
146+ _running = false ;
126147 }
127148
128- if ( _task == null )
149+ _stoppingCts . Cancel ( ) ;
150+
151+ _timer . Dispose ( ) ;
152+
153+ if ( _task == null )
129154 {
130- return Task . CompletedTask ;
155+ return ;
131156 }
132157
133- return _task ;
158+ try
159+ {
160+ await _task ;
161+ }
162+ catch ( OperationCanceledException )
163+ {
164+ }
134165 }
135166
136167 /// <summary>
137168 /// Release all resources used by the current instance of <see cref="PeriodicTask"/>
138169 /// </summary>
139170 public void Dispose ( )
140171 {
141- _stoppingCts ? . Dispose ( ) ;
172+ lock ( _runningLock )
173+ {
174+ _running = false ;
175+ }
176+
177+ _stoppingCts . Dispose ( ) ;
178+
179+ _timer . Dispose ( ) ;
142180 }
143181
144182 #endregion
@@ -169,27 +207,24 @@ private async Task taskRunner()
169207 }
170208 }
171209
172- while ( _stoppingCts . Token . IsCancellationRequested == false )
210+ while ( await _timer . WaitForNextTickAsync ( _stoppingCts . Token ) == true )
173211 {
174- try
175- {
176- await _action ( _stoppingCts . Token ) . ConfigureAwait ( false ) ;
177- }
178- catch ( OperationCanceledException )
212+ lock ( _runningLock )
179213 {
180- if ( _stoppingCts . IsCancellationRequested )
214+ if ( _running == false )
181215 {
182- throw ;
216+ return ;
183217 }
184218 }
185- catch ( Exception e )
219+
220+ if ( _stoppingCts . IsCancellationRequested == true )
186221 {
187- Logger . LogCritical ( e , "Unhandled Exception on the Periodic Task Action Method" ) ;
222+ return ;
188223 }
189-
224+
190225 try
191226 {
192- await Task . Delay ( _interval , _stoppingCts . Token ) . ConfigureAwait ( false ) ;
227+ await _action ( _stoppingCts . Token ) . ConfigureAwait ( false ) ;
193228 }
194229 catch ( OperationCanceledException )
195230 {
@@ -198,8 +233,9 @@ private async Task taskRunner()
198233 throw ;
199234 }
200235 }
201- catch
236+ catch ( Exception e )
202237 {
238+ Logger . LogCritical ( e , "Unhandled Exception on the Periodic Task Action Method" ) ;
203239 }
204240 }
205241 }
0 commit comments