Skip to content

Commit 20f6ce3

Browse files
committed
Improved PeriodicTask to use .NET 6 PeriodicTimer under the hood
1 parent 83094bd commit 20f6ce3

3 files changed

Lines changed: 71 additions & 33 deletions

File tree

RICADO.Threading/PeriodicFactory.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public PeriodicFactory()
3131
#region Public Methods
3232

3333
/// <summary>
34-
/// Create a new <see cref="PeriodicTimer"/>
34+
/// Create a new <see cref="PeriodicSyncTimer"/>
3535
/// </summary>
3636
/// <param name="name">A Name for the Timer</param>
3737
/// <param name="action">The Method to be periodically called</param>
@@ -40,7 +40,7 @@ public PeriodicFactory()
4040
/// <returns>The Name of the new Timer</returns>
4141
/// <exception cref="System.ArgumentNullException"></exception>
4242
/// <exception cref="System.ArgumentException"></exception>
43-
public string CreateNew(string name, Action action, int interval, int startDelay = 0)
43+
public string CreateNew(string? name, Action action, int interval, int startDelay = 0)
4444
{
4545
if(name == null)
4646
{
@@ -57,13 +57,13 @@ public string CreateNew(string name, Action action, int interval, int startDelay
5757
throw new ArgumentException("The Specified Name has already been used by another Periodic Item", nameof(name));
5858
}
5959

60-
_periodicItems.TryAdd(name, new PeriodicTimer(action, interval, startDelay));
60+
_periodicItems.TryAdd(name, new PeriodicSyncTimer(action, interval, startDelay));
6161

6262
return name;
6363
}
6464

6565
/// <summary>
66-
/// Create a new <see cref="PeriodicTimer"/>
66+
/// Create a new <see cref="PeriodicSyncTimer"/>
6767
/// </summary>
6868
/// <param name="action">The Method to be periodically called</param>
6969
/// <param name="interval">The Interval between Method calls in Milliseconds</param>
@@ -86,7 +86,7 @@ public string CreateNew(Action action, int interval, int startDelay = 0)
8686
/// <returns>The Name of the new Task</returns>
8787
/// <exception cref="System.ArgumentNullException"></exception>
8888
/// <exception cref="System.ArgumentException"></exception>
89-
public string CreateNew(string name, Func<CancellationToken, Task> action, int interval, int startDelay = 0)
89+
public string CreateNew(string? name, Func<CancellationToken, Task> action, int interval, int startDelay = 0)
9090
{
9191
if (name == null)
9292
{

RICADO.Threading/PeriodicTask.cs

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

RICADO.Threading/RICADO.Threading.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
<IncludeSymbols>true</IncludeSymbols>
1313
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
1414
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
15+
<ImplicitUsings>enable</ImplicitUsings>
16+
<Nullable>enable</Nullable>
1517
</PropertyGroup>
1618

1719
<ItemGroup>

0 commit comments

Comments
 (0)