AbpTimer.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. using System;
  2. using System.Threading;
  3. using Abp.Dependency;
  4. namespace Abp.Threading.Timers
  5. {
  6. /// <summary>
  7. /// A roboust timer implementation that ensures no overlapping occurs. It waits exactly specified <see cref="Period"/> between ticks.
  8. /// </summary>
  9. //TODO: Extract interface or make all members virtual to make testing easier.
  10. public class AbpTimer : RunnableBase, ITransientDependency
  11. {
  12. /// <summary>
  13. /// This event is raised periodically according to Period of Timer.
  14. /// </summary>
  15. public event EventHandler Elapsed;
  16. /// <summary>
  17. /// Task period of timer (as milliseconds).
  18. /// </summary>
  19. public int Period { get; set; }
  20. /// <summary>
  21. /// Indicates whether timer raises Elapsed event on Start method of Timer for once.
  22. /// Default: False.
  23. /// </summary>
  24. public bool RunOnStart { get; set; }
  25. /// <summary>
  26. /// This timer is used to perfom the task at spesified intervals.
  27. /// </summary>
  28. private readonly Timer _taskTimer;
  29. /// <summary>
  30. /// Indicates that whether timer is running or stopped.
  31. /// </summary>
  32. private volatile bool _running;
  33. /// <summary>
  34. /// Indicates that whether performing the task or _taskTimer is in sleep mode.
  35. /// This field is used to wait executing tasks when stopping Timer.
  36. /// </summary>
  37. private volatile bool _performingTasks;
  38. /// <summary>
  39. /// Creates a new Timer.
  40. /// </summary>
  41. public AbpTimer()
  42. {
  43. _taskTimer = new Timer(TimerCallBack, null, Timeout.Infinite, Timeout.Infinite);
  44. }
  45. /// <summary>
  46. /// Creates a new Timer.
  47. /// </summary>
  48. /// <param name="period">Task period of timer (as milliseconds)</param>
  49. /// <param name="runOnStart">Indicates whether timer raises Elapsed event on Start method of Timer for once</param>
  50. public AbpTimer(int period, bool runOnStart = false)
  51. : this()
  52. {
  53. Period = period;
  54. RunOnStart = runOnStart;
  55. }
  56. /// <summary>
  57. /// Starts the timer.
  58. /// </summary>
  59. public override void Start()
  60. {
  61. if (Period <= 0)
  62. {
  63. throw new AbpException("Period should be set before starting the timer!");
  64. }
  65. base.Start();
  66. _running = true;
  67. _taskTimer.Change(RunOnStart ? 0 : Period, Timeout.Infinite);
  68. }
  69. /// <summary>
  70. /// Stops the timer.
  71. /// </summary>
  72. public override void Stop()
  73. {
  74. lock (_taskTimer)
  75. {
  76. _running = false;
  77. _taskTimer.Change(Timeout.Infinite, Timeout.Infinite);
  78. }
  79. base.Stop();
  80. }
  81. /// <summary>
  82. /// Waits the service to stop.
  83. /// </summary>
  84. public override void WaitToStop()
  85. {
  86. lock (_taskTimer)
  87. {
  88. while (_performingTasks)
  89. {
  90. Monitor.Wait(_taskTimer);
  91. }
  92. }
  93. base.WaitToStop();
  94. }
  95. /// <summary>
  96. /// This method is called by _taskTimer.
  97. /// </summary>
  98. /// <param name="state">Not used argument</param>
  99. private void TimerCallBack(object state)
  100. {
  101. lock (_taskTimer)
  102. {
  103. if (!_running || _performingTasks)
  104. {
  105. return;
  106. }
  107. _taskTimer.Change(Timeout.Infinite, Timeout.Infinite);
  108. _performingTasks = true;
  109. }
  110. try
  111. {
  112. if (Elapsed != null)
  113. {
  114. Elapsed(this, new EventArgs());
  115. }
  116. }
  117. catch
  118. {
  119. }
  120. finally
  121. {
  122. lock (_taskTimer)
  123. {
  124. _performingTasks = false;
  125. if (_running)
  126. {
  127. _taskTimer.Change(Period, Timeout.Infinite);
  128. }
  129. Monitor.Pulse(_taskTimer);
  130. }
  131. }
  132. }
  133. }
  134. }