AbstractWorker.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. using System;
  2. using System.Threading;
  3. using System.Threading.Tasks;
  4. using Quadarax.Foundation.Core.Logging;
  5. using Quadarax.Foundation.Core.Object;
  6. using Quadarax.Foundation.Core.Thread.Extensions;
  7. using Quadarax.Foundation.Core.Value.Extensions;
  8. namespace Quadarax.Foundation.Core.Thread
  9. {
  10. public abstract class AbstractWorker : DisposableObject, IWorker
  11. {
  12. #region *** Private fields ***
  13. private Task _thread;
  14. private IQLog _log;
  15. private TimeSpan _delay;
  16. private CancellationTokenSource _cancel;
  17. protected IQLogger _logger;
  18. #endregion
  19. #region *** Properties ***
  20. public bool IsRunning { get; private set; }
  21. public bool IsTransient { get; private set; }
  22. public string Name { get; }
  23. public int CurrentThreadId => System.Threading.Thread.CurrentThread.ManagedThreadId;
  24. public TimeSpan Timeout { get; set; }
  25. protected IWorkerContext Context { get; private set; }
  26. public long IterationCount { get; protected set; } = 0;
  27. #endregion
  28. #region *** Constructors ***
  29. protected AbstractWorker() : this($"Worker#{Guid.NewGuid().ToBase32String()}", null)
  30. {
  31. }
  32. protected AbstractWorker(IWorkerContext context) : this($"Worker#{Guid.NewGuid().ToBase32String()}", context)
  33. {
  34. }
  35. protected AbstractWorker(string workerName) : this(workerName, null)
  36. {
  37. }
  38. protected AbstractWorker(string workerName, IWorkerContext context, IQLogger logger = null)
  39. {
  40. if (string.IsNullOrEmpty(workerName))
  41. throw new ArgumentNullException(nameof(workerName));
  42. Name = workerName;
  43. Context = context;
  44. _log = logger?.GetLogger(GetType());
  45. _logger = logger;
  46. Timeout = TimeSpan.Zero;
  47. }
  48. #endregion
  49. #region *** Public Operations ***
  50. public void Start(bool silentlyIfNotValidState = false)
  51. {
  52. Start(TimeSpan.Zero, silentlyIfNotValidState);
  53. }
  54. public void Start(TimeSpan runDelay, bool silentlyIfNotValidState = false)
  55. {
  56. if (IsTransient || IsRunning)
  57. if (!silentlyIfNotValidState)
  58. throw new InvalidOperationException(
  59. $"Worker '{Name}' is in transient or running state. Cannot start.");
  60. else
  61. return;
  62. OnBeforeStart();
  63. _cancel = new CancellationTokenSource();
  64. _delay = runDelay;
  65. _thread = Task.Factory.StartNew(() => DoInternal(this), _cancel.Token);
  66. IsRunning = true;
  67. }
  68. public void Stop(bool silentlyIfNotValidState = false)
  69. {
  70. if (IsTransient || !IsRunning)
  71. if (!silentlyIfNotValidState)
  72. throw new InvalidOperationException(
  73. $"Worker '{Name}' is in transient or running state. Cannot start.");
  74. else
  75. return;
  76. IsTransient = true;
  77. Abort();
  78. OnAfterStop();
  79. }
  80. #endregion
  81. #region *** Virtuals and protected ***
  82. protected virtual void OnBeforeStart()
  83. {
  84. }
  85. protected virtual void OnAfterStop()
  86. {
  87. }
  88. protected virtual async Task Do(IWorkerContext context)
  89. {
  90. throw new NotImplementedException("AbstractWorker.Do() must be overriden.");
  91. }
  92. protected void Log(LogSeverityEnum severity, string message)
  93. {
  94. _log?.Log(severity, message);
  95. }
  96. protected void Log(LogSeverityEnum severity, string message, Exception exception)
  97. {
  98. _log?.Log(severity, message, exception);
  99. }
  100. protected override void OnDisposing()
  101. {
  102. Abort();
  103. Context = null;
  104. _log = null;
  105. _logger = null;
  106. }
  107. #endregion
  108. #region *** Private Operations ***
  109. private async void DoInternal(object context)
  110. {
  111. var wk = (AbstractWorker)context;
  112. if (wk._delay != TimeSpan.Zero)
  113. wk._thread.Wait(wk._delay);
  114. wk.IsTransient = false;
  115. try
  116. {
  117. if (Timeout == TimeSpan.Zero)
  118. await wk.Do(wk.Context);
  119. else
  120. await wk.Do(wk.Context).TimeoutAfter(Timeout);
  121. IterationCount++;
  122. }
  123. catch (Exception e)
  124. {
  125. wk.Log(LogSeverityEnum.Error, $"Worker '{Name}' failed.", e);
  126. throw;
  127. }
  128. finally
  129. {
  130. wk.IsRunning = false;
  131. }
  132. }
  133. private void Abort()
  134. {
  135. if (_thread == null)
  136. return;
  137. _cancel.Cancel();
  138. _cancel.Dispose();
  139. _cancel = null;
  140. _thread = null;
  141. IsRunning = false;
  142. IsTransient = false;
  143. }
  144. #endregion
  145. }
  146. }