Worker.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. namespace MeloongCore;
  2. /// <summary>
  3. /// 可合并和重启的工作器。
  4. /// <para/> 在工作负载运行期间多次调用 <see cref="Run()"/> 会取消当前负载并使用最新令牌重新执行;多次调用也只重新执行一次。
  5. /// <para/> 若即将重启,则忽略当前负载的异常。
  6. /// </summary>
  7. public interface IRedoableWorker {
  8. /// <summary>当前是否正在运行。</summary>
  9. bool Running { get; }
  10. /// <summary>是否曾经有过未被取消且未失败的运行。</summary>
  11. bool HasSucceeded { get; }
  12. /// <summary>标识上次进入空闲时,并非因为取消或失败,而是正常运行到结束。</summary>
  13. bool LastSucceeded { get; }
  14. /// <summary>在工作线程运行工作负载。若当前已在运行,则取消当前负载并使用最新令牌重启。</summary>
  15. void Run(CancellationToken cancellationToken = default);
  16. /// <summary>取消当前运行。</summary>
  17. void Cancel();
  18. /// <summary>仅在当前处于运行状态时,等待其完成。</summary>
  19. /// <returns>若未超时则返回 true。</returns>
  20. bool WaitIfRunning(int millisecondsTimeout = -1);
  21. }
  22. /// <inheritdoc cref="IRedoableWorker"/>
  23. public abstract class RedoableWorkerBase<TOut>(Func<CancellationToken, TOut> workload) : IRedoableWorker {
  24. // ============================================ 状态 ============================================
  25. /// <inheritdoc/>
  26. public bool Running { get { lock (this) return running; } }
  27. private bool running;
  28. /// <inheritdoc/>
  29. public bool HasSucceeded { get { lock (this) return hasSucceeded; } }
  30. private bool hasSucceeded;
  31. /// <inheritdoc/>
  32. public bool LastSucceeded { get { lock (this) return lastSucceeded; } }
  33. private bool lastSucceeded;
  34. /// <summary>上次未被取消且未失败的运行中,工作负载的返回值。</summary>
  35. public TOut LastResult {
  36. get {
  37. lock (this) {
  38. if (!hasSucceeded) throw new InvalidOperationException("从未成功完成过。");
  39. return lastResult == null ? default! : (TOut) lastResult;
  40. }
  41. }
  42. }
  43. private object? lastResult;
  44. private bool pendingRedo;
  45. // =========================================== 运行与取消 ===========================================
  46. private CancellationTokenSource? realCts;
  47. private CancellationToken lastToken;
  48. private readonly ManualResetEventSlim idleEvent = new(initialState: true);
  49. /// <inheritdoc/>
  50. public void Run(CancellationToken cancellationToken = default) {
  51. // 接取运行状态
  52. lock (this) {
  53. lastToken = cancellationToken;
  54. if (running) {
  55. pendingRedo = true;
  56. realCts?.Cancel();
  57. return;
  58. }
  59. running = true; idleEvent.Reset();
  60. realCts = CancellationTokenSource.CreateLinkedTokenSource(lastToken);
  61. }
  62. var th = new Thread(_Run);
  63. th.Start();
  64. }
  65. private void _Run() {
  66. try {
  67. TOut result = workload(realCts!.Token); // 实际的执行
  68. realCts.Token.ThrowIfCancellationRequested();
  69. lock (this) {
  70. realCts?.Dispose();
  71. if (pendingRedo) { // 接取重启请求
  72. pendingRedo = false;
  73. realCts = CancellationTokenSource.CreateLinkedTokenSource(lastToken);
  74. _Run(); return;
  75. } else {
  76. realCts = null;
  77. }
  78. running = false; idleEvent.Set();
  79. lastSucceeded = true;
  80. lastResult = result;
  81. hasSucceeded = true;
  82. }
  83. } catch (Exception ex) {
  84. lock (this) {
  85. realCts?.Dispose();
  86. if (pendingRedo) { // 接取重启请求
  87. pendingRedo = false;
  88. realCts = CancellationTokenSource.CreateLinkedTokenSource(lastToken);
  89. if (!ex.IsCanceled()) Logger.Info(ex, "在重启前出现了异常");
  90. _Run(); return;
  91. } else {
  92. realCts = null;
  93. }
  94. running = false; idleEvent.Set();
  95. lastSucceeded = false;
  96. }
  97. if (!ex.IsCanceled()) Logger.Error(ex, "工作线程执行失败", LogBehavior.None);
  98. }
  99. }
  100. /// <inheritdoc/>
  101. public void Cancel() {
  102. lock (this) {
  103. pendingRedo = false;
  104. realCts?.Cancel();
  105. }
  106. }
  107. /// <inheritdoc/>
  108. public bool WaitIfRunning(int millisecondsTimeout = -1)
  109. => idleEvent.Wait(millisecondsTimeout);
  110. }
  111. /// <inheritdoc />
  112. public sealed class RedoableWorker<TOut> : RedoableWorkerBase<TOut> {
  113. public RedoableWorker(Func<CancellationToken, TOut> workload) : base(workload) { }
  114. public RedoableWorker(Func<TOut> workload) : base(_ => workload()) { }
  115. }
  116. /// <inheritdoc />
  117. public sealed class RedoableWorker : RedoableWorkerBase<object?> {
  118. public RedoableWorker(Action<CancellationToken> workload) : base(ct => { workload(ct); return null; }) { }
  119. public RedoableWorker(Action workload) : base(_ => { workload(); return null; }) { }
  120. }