条件监视器
一、功能说明与定义
条件监视器:监测(循环判断)某一条件是否成立,成立则退出监测,备选条件,有监测总时间或总次数的限制。
二、产生背景
把文件从xps转成pdf的时候需要判断这份xps文件是否已经生成完成,且不被占用。由于xps生成的某种原因,生成完成xps时文件还处于占用状态,无法立即进行处理转换成pdf,在此前提下此功能与需求孕育而生。
三、实现
1、ConditionLooper.cs
1 /// <summary> 2 /// 条件循环器 3 /// </summary> 4 internal class ConditionLooper : IDisposable 5 { 6 /// <summary> 7 /// 时间间隔,默认100毫秒(以毫秒为单位) 8 /// </summary> 9 public int Interval = 100; 10 /// <summary> 11 /// 首次调用是否延迟<see cref="Interval"/>指定的时间间隔,默认为<seealso cref="false"/> 12 /// </summary> 13 public bool IsFirstDelay = false; 14 /// <summary> 15 /// 是否正在运行 16 /// </summary> 17 public bool IsRunning = false; 18 /// <summary> 19 /// 超时时间(以毫秒为单位),默认为 -1 不启用 20 /// </summary> 21 public int TimeOut = -1; 22 /// <summary> 23 /// 循环执行次数,默认为 -1 不启用 24 /// </summary> 25 public int LoopTimes = -1; 26 /// <summary> 27 /// <see cref="WorkCheck"/>的执行结果,<seealso cref="ConditionLooperResult"/>类型枚举值。 28 /// </summary> 29 public ConditionLooperResult Result = ConditionLooperResult.None; 30 /// <summary> 31 /// 周期循环的检查任务,返回<see cref="true"/>时则会终止循环检测 32 /// </summary> 33 public event Func<bool> WorkCheck; 34 /// <summary> 35 /// 结束循环调用触发的事件 36 /// </summary> 37 public event Action<ConditionLooperResult> EndLoop; 38 protected bool _enable; 39 /// <summary> 40 /// 启用/停用 41 /// </summary> 42 public virtual bool Enable 43 { 44 get 45 { 46 return this._enable; 47 } 48 set 49 { 50 if (this._enable != value) 51 { 52 this._enable = value; 53 } 54 55 if (this._enable) 56 { 57 this.Log($"{string.Format("{0,-4}", System.Threading.Thread.CurrentThread.ManagedThreadId)} -> 启动循环任务...."); 58 this.Log($"{string.Format("{0,-4}", System.Threading.Thread.CurrentThread.ManagedThreadId)}Interval = {this.Interval},TimeOut = {this.TimeOut},LoopTimes = {this.LoopTimes},IsFirstDelay = {this.IsFirstDelay}"); 59 this.Start((state) => 60 { 61 //Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} -> ManagedThreadId = {System.Threading.Thread.CurrentThread.ManagedThreadId}."); 62 // 执行任务 63 var matched = (this.WorkCheck?.Invoke()).GetValueOrDefault(false); 64 if (matched) 65 { 66 // 满足条件,则终止 67 this.Log($"{string.Format("{0,-4}", System.Threading.Thread.CurrentThread.ManagedThreadId)} -> 满足条件终止。"); 68 this.EndLoopInternal(ConditionLooperResult.Matched); 69 return; 70 } 71 72 // 启用超时设置时进行超时判断 73 if (this.IsEnableTimeOut) 74 { 75 this.Log($"{string.Format("{0,-4}", System.Threading.Thread.CurrentThread.ManagedThreadId)} -> ElapsedMilliseconds = {this.TotalTimeWatch.ElapsedMilliseconds}/{this.TimeOut}."); 76 if (this.TotalTimeWatch.ElapsedMilliseconds >= this.TimeOut) 77 { 78 // 超时 79 this.Log($"{string.Format("{0,-4}", System.Threading.Thread.CurrentThread.ManagedThreadId)} -> 超时退出。"); 80 this.EndLoopInternal(ConditionLooperResult.TimeOut); 81 return; 82 } 83 } 84 85 if (this.IsEnableLoopTimes) 86 { 87 // 调用次数 +1 88 this.ExecuteTime++; 89 90 this.Log($"{string.Format("{0,-4}", System.Threading.Thread.CurrentThread.ManagedThreadId)} -> ExecuteTime = {this.ExecuteTime}/{this.LoopTimes}."); 91 if (this.ExecuteTime >= this.LoopTimes) 92 { 93 // 超过次数限制 94 this.Log($"{string.Format("{0,-4}", System.Threading.Thread.CurrentThread.ManagedThreadId)} -> 超过次数限制退出。"); 95 this.EndLoopInternal(ConditionLooperResult.TimesLimited); 96 return; 97 } 98 } 99 }); 100 } 101 else 102 { 103 this.Log($"{string.Format("{0,-4}", System.Threading.Thread.CurrentThread.ManagedThreadId)} -> 停止循环任务...."); 104 this.Stop(); 105 } 106 } 107 } 108 109 110 /// <summary> 111 /// 定时任务 112 /// </summary> 113 private System.Threading.Timer JobTimer; 114 /// <summary> 115 /// 总耗时计时器 116 /// </summary> 117 private Stopwatch TotalTimeWatch; 118 /// <summary> 119 /// 记录调用次数 120 /// </summary> 121 private int ExecuteTime = 0; 122 /// <summary> 123 /// 是否启用异步调用 124 /// </summary> 125 private bool IsAsyncCall = false; 126 /// <summary> 127 /// 等待信号,false - 默认等待;ture - 默认放行 128 /// </summary> 129 private AutoResetEvent WaitSignal = new AutoResetEvent(false); 130 /// <summary> 131 /// 判断是否启用超时时间设置 132 /// </summary> 133 private bool IsEnableTimeOut 134 { 135 get 136 { 137 return (this.TimeOut > 0); 138 } 139 } 140 /// <summary> 141 /// 判断是否启用次数限制设置 142 /// </summary> 143 private bool IsEnableLoopTimes 144 { 145 get 146 { 147 return (this.LoopTimes > 0); 148 } 149 } 150 151 /// <summary> 152 /// 执行检查任务(同步调用) 153 /// </summary> 154 /// <param name="checker"></param> 155 /// <returns></returns> 156 public ConditionLooperResult Run(Func<bool> @checker) 157 { 158 this.IsAsyncCall = false; 159 this.WorkCheck = @checker; 160 this.Enable = true; 161 162 // 等待放行信号 163 this.WaitSignal.WaitOne(); 164 165 return this.Result; 166 } 167 /// <summary> 168 /// 执行检查任务(异步调用) 169 /// </summary> 170 /// <param name="checker"></param> 171 /// <param name="callback"></param> 172 public void RunAsync(Func<bool> @checker, Action<ConditionLooperResult> callback) 173 { 174 this.IsAsyncCall = true; 175 this.WorkCheck = @checker; 176 this.EndLoop = callback; 177 this.Enable = true; 178 } 179 /// <summary> 180 /// 释放资源 181 /// </summary> 182 public virtual void Dispose() 183 { 184 // 停止计时器 185 this.StopTimer(); 186 187 if (null != this.WaitSignal) 188 { 189 this.WaitSignal.Dispose(); 190 this.WaitSignal = null; 191 } 192 } 193 194 /// <summary> 195 /// 停止计时器 196 /// </summary> 197 private void StopTimer() 198 { 199 if (null != this.JobTimer) 200 { 201 this.JobTimer.Dispose(); 202 this.JobTimer = null; 203 } 204 } 205 /// <summary> 206 /// 停止任务 207 /// </summary> 208 protected virtual void Stop() 209 { 210 this.IsRunning = false; 211 212 // 停止计时器 213 this.StopTimer(); 214 215 if (null != this.TotalTimeWatch) 216 { 217 this.TotalTimeWatch.Stop(); 218 } 219 220 // 信号放行 221 if (!this.IsAsyncCall) 222 { 223 this.WaitSignal.Set(); 224 } 225 } 226 /// <summary> 227 /// 启动任务(创建并启动计时器) 228 /// </summary> 229 /// <param name="callback"></param> 230 protected virtual void Start(System.Threading.TimerCallback callback) 231 { 232 if ((null != this.JobTimer)) 233 { 234 // 停止计时器 235 this.StopTimer(); 236 } 237 238 // 重置运行状态 239 this.Reset(); 240 this.JobTimer = new System.Threading.Timer(callback, null, (this.IsFirstDelay ? this.Interval : 0), this.Interval); 241 } 242 /// <summary> 243 /// 重置运行状态 244 /// </summary> 245 protected virtual void Reset() 246 { 247 this.Result = ConditionLooperResult.None; 248 249 if (!this.IsAsyncCall) 250 { 251 // 设置为非终止,导致外部线程阻塞,等待任务完成或超时 252 this.WaitSignal.Reset(); 253 } 254 255 if (this.IsEnableTimeOut) 256 { 257 if (null == this.TotalTimeWatch) 258 { 259 this.TotalTimeWatch = new Stopwatch(); 260 } 261 else 262 { 263 this.TotalTimeWatch.Reset(); 264 } 265 266 this.TotalTimeWatch.Start(); 267 } 268 269 if (this.IsEnableLoopTimes) 270 { 271 this.ExecuteTime = 0; 272 } 273 this.IsRunning = true; 274 } 275 /// <summary> 276 /// 触发结束事件 277 /// </summary> 278 /// <param name="result">结果</param> 279 protected virtual void EndLoopInternal(ConditionLooperResult result) 280 { 281 // 保存结果 282 this.Result = result; 283 // 执行停止 284 this.Stop(); 285 286 // 触发结束事件 287 this.EndLoop?.Invoke(result); 288 } 289 /// <summary> 290 /// 记录日志 291 /// </summary> 292 /// <param name="content"></param> 293 [Conditional("DEBUG")] 294 protected virtual void Log(string content) 295 { 296 LogProvider.Log.Debug(content); 297 298 //System.Diagnostics.Debug.WriteLine(content); 299 //Console.WriteLine(content); 300 } 301 }
2、ConditionLooperResult类
1 /// <summary> 2 /// 循环调用结果 3 /// </summary> 4 public enum ConditionLooperResult 5 { 6 /// <summary> 7 /// 默认状态 8 /// </summary> 9 None = 0, 10 /// <summary> 11 /// 匹配 12 /// </summary> 13 Matched = 1, 14 /// <summary> 15 /// 超时 16 /// </summary> 17 TimeOut = 2, 18 /// <summary> 19 /// 超过次数限制 20 /// </summary> 21 TimesLimited = 3 22 }
3、ConditionMonitor类
1 /// <summary> 2 /// 条件监视器 3 /// </summary> 4 public static class ConditionMonitor 5 { 6 /// <summary> 7 /// 周期性检测<paramref name="condition"/>条件表达式是否满足,直到满足条件或超时时间已到则退出。 8 /// </summary> 9 /// <param name="condition">条件表达式。</param> 10 /// <param name="interval">检测时间间隔,默认100毫秒。</param> 11 /// <param name="timeOut">检测超时时间,-1 为不启用,<paramref name="condition"/>表示的条件表达满足时才会退出。</param> 12 /// <param name="isDelay">首次调用是否延迟<paramref name="interval"/>指定的时间间隔,默认为<seealso cref="false"/></param> 13 /// <returns>检测结果</returns> 14 public static ConditionLooperResult Wait(Func<bool> condition, int interval = 100, int timeOut = -1, bool isDelay = false) 15 { 16 using (var looper = new ConditionLooper() { Interval = interval, TimeOut = timeOut, IsFirstDelay = isDelay }) 17 { 18 return looper.Run(condition); 19 } 20 } 21 22 /// <summary> 23 /// 周期性检测<paramref name="condition"/>条件表达式是否满足,直到满足条件或循环检测次数已到则退出。 24 /// </summary> 25 /// <param name="condition">条件表达式。</param> 26 /// <param name="interval">检测时间间隔,默认1000毫秒即1秒。</param> 27 /// <param name="loopTimes">循环检测的次数,-1 为不启用,<paramref name="condition"/>表示的条件表达满足时才会退出。</param> 28 /// <param name="isDelay">首次调用是否延迟<paramref name="interval"/>指定的时间间隔,默认为<seealso cref="false"/></param> 29 /// <returns></returns> 30 public static ConditionLooperResult WaitForTimes(Func<bool> condition, int interval = 1000, int loopTimes = -1, bool isDelay = false) 31 { 32 using (var looper = new ConditionLooper() { Interval = interval, LoopTimes = loopTimes, IsFirstDelay = isDelay }) 33 { 34 return looper.Run(condition); 35 } 36 } 37 38 /// <summary> 39 /// 异步周期性检测<paramref name="condition"/>条件表达式是否满足,直到满足条件或超时时间已到则退出。 40 /// </summary> 41 /// <param name="condition">条件表达式。</param> 42 /// <param name="callback">退出时的回调函数。</param> 43 /// <param name="interval">检测时间间隔,默认100毫秒。</param> 44 /// <param name="timeOut">检测超时时间,-1 为不启用,<paramref name="condition"/>表示的条件表达满足时才会退出。</param> 45 /// <param name="isDelay">首次调用是否延迟<paramref name="interval"/>指定的时间间隔,默认为<seealso cref="false"/></param> 46 /// <returns>检测结果</returns> 47 public static void WaitAsync(Func<bool> condition, Action<ConditionLooperResult> callback, int interval = 100, int timeOut = -1, bool isDelay = false) 48 { 49 var looper = new ConditionLooper() { Interval = interval, TimeOut = timeOut, IsFirstDelay = isDelay }; 50 51 looper.RunAsync(condition, (result) => 52 { 53 callback?.Invoke(result); 54 looper.Dispose(); 55 }); 56 } 57 58 /// <summary> 59 /// 异步周期性检测<paramref name="condition"/>条件表达式是否满足,直到满足条件或循环检测次数已到则退出,通过回调函数返回执行结果。 60 /// </summary> 61 /// <param name="condition">条件表达式。</param> 62 /// <param name="callback">退出时的回调函数。</param> 63 /// <param name="interval">检测时间间隔,默认1000毫秒即1秒。</param> 64 /// <param name="loopTimes">循环检测的次数,-1 为不启用,<paramref name="condition"/>表示的条件表达满足时才会退出。</param> 65 /// <param name="isDelay">首次调用是否延迟<paramref name="interval"/>指定的时间间隔,默认为<seealso cref="false"/></param> 66 /// <returns></returns> 67 public static void WaitForTimesAsync(Func<bool> condition, Action<ConditionLooperResult> callback, int interval = 100, int loopTimes = -1, bool isDelay = false) 68 { 69 var looper = new ConditionLooper() { Interval = interval, LoopTimes = loopTimes, IsFirstDelay = isDelay }; 70 71 looper.RunAsync(condition, (result) => 72 { 73 callback?.Invoke(result); 74 looper.Dispose(); 75 }); 76 } 77 }
4、判断文件是否被占用 函数
1 /// <summary> 2 /// 判断文件是否被占用 3 /// </summary> 4 /// <param name="path"></param> 5 /// <returns></returns> 6 public static bool IsFileUsing(string path) 7 { 8 if (!File.Exists(path)) 9 { 10 // 文件不存在,则等待文件生成 11 return true; 12 } 13 14 var used = true; 15 FileStream fs = null; 16 17 try 18 { 19 20 fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None); 21 22 Debug.WriteLine($"not used."); 23 used = false; 24 } 25 catch 26 { 27 } 28 finally 29 { 30 if (fs != null) 31 { 32 fs.Close(); 33 } 34 } 35 36 return used; 37 }
5、测试代码段
1 Console.WriteLine($"{string.Format("{0,-4}", System.Threading.Thread.CurrentThread.ManagedThreadId)}{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} -> 启动控制台"); 2 3 var filePath = AppDomain.CurrentDomain.BaseDirectory + "abc.txt"; 4 ConditionMonitor.Wait(() => 5 { 6 Console.WriteLine($"{string.Format("{0,-4}", System.Threading.Thread.CurrentThread.ManagedThreadId)}->{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); 7 return !ConditionMonitor.IsFileUsing(filePath); 8 }, 1000, -1, true);
6、测试效果