mirror of https://github.com/abpframework/abp.git
7 changed files with 171 additions and 35 deletions
@ -0,0 +1,127 @@ |
|||||
|
using System; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.ExceptionHandling; |
||||
|
|
||||
|
namespace Volo.Abp.Threading |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A robust timer implementation that ensures no overlapping occurs. It waits exactly specified <see cref="Period"/> between ticks.
|
||||
|
/// </summary>
|
||||
|
public class AbpAsyncTimer : ITransientDependency |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// This func is raised periodically according to Period of Timer.
|
||||
|
/// </summary>
|
||||
|
public Func<AbpAsyncTimer, Task> Elapsed = _ => Task.CompletedTask; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Task period of timer (as milliseconds).
|
||||
|
/// </summary>
|
||||
|
public int Period { get; set; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Indicates whether timer raises Elapsed event on Start method of Timer for once.
|
||||
|
/// Default: False.
|
||||
|
/// </summary>
|
||||
|
public bool RunOnStart { get; set; } |
||||
|
|
||||
|
public ILogger<AbpAsyncTimer> Logger { get; set; } |
||||
|
|
||||
|
public IExceptionNotifier ExceptionNotifier { get; set; } |
||||
|
|
||||
|
private readonly Timer _taskTimer; |
||||
|
private volatile bool _performingTasks; |
||||
|
private volatile bool _isRunning; |
||||
|
|
||||
|
public AbpAsyncTimer() |
||||
|
{ |
||||
|
ExceptionNotifier = NullExceptionNotifier.Instance; |
||||
|
Logger = NullLogger<AbpAsyncTimer>.Instance; |
||||
|
|
||||
|
_taskTimer = new Timer( |
||||
|
TimerCallBack, |
||||
|
null, |
||||
|
Timeout.Infinite, |
||||
|
Timeout.Infinite |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public void Start(CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
if (Period <= 0) |
||||
|
{ |
||||
|
throw new AbpException("Period should be set before starting the timer!"); |
||||
|
} |
||||
|
|
||||
|
lock (_taskTimer) |
||||
|
{ |
||||
|
_taskTimer.Change(RunOnStart ? 0 : Period, Timeout.Infinite); |
||||
|
_isRunning = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Stop(CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
lock (_taskTimer) |
||||
|
{ |
||||
|
_taskTimer.Change(Timeout.Infinite, Timeout.Infinite); |
||||
|
while (_performingTasks) |
||||
|
{ |
||||
|
Monitor.Wait(_taskTimer); |
||||
|
} |
||||
|
|
||||
|
_isRunning = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// This method is called by _taskTimer.
|
||||
|
/// </summary>
|
||||
|
/// <param name="state">Not used argument</param>
|
||||
|
private void TimerCallBack(object state) |
||||
|
{ |
||||
|
lock (_taskTimer) |
||||
|
{ |
||||
|
if (!_isRunning || _performingTasks) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
_taskTimer.Change(Timeout.Infinite, Timeout.Infinite); |
||||
|
_performingTasks = true; |
||||
|
} |
||||
|
|
||||
|
_ = Timer_Elapsed(); |
||||
|
} |
||||
|
|
||||
|
private async Task Timer_Elapsed() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
await Elapsed(this); |
||||
|
} |
||||
|
catch(Exception ex) |
||||
|
{ |
||||
|
Logger.LogException(ex); |
||||
|
await ExceptionNotifier.NotifyAsync(ex); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
lock (_taskTimer) |
||||
|
{ |
||||
|
_performingTasks = false; |
||||
|
if (_isRunning) |
||||
|
{ |
||||
|
_taskTimer.Change(Period, Timeout.Infinite); |
||||
|
} |
||||
|
|
||||
|
Monitor.Pulse(_taskTimer); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue