条件监视器
一、功能说明与定义
条件监视器:监测(循环判断)某一条件是否成立,成立则退出监测,备选条件,有监测总时间或总次数的限制。
二、产生背景
把文件从xps转成pdf的时候需要判断这份xps文件是否已经生成完成,且不被占用。由于xps生成的某种原因,生成完成xps时文件还处于占用状态,无法立即进行处理转换成pdf,在此前提下此功能与需求孕育而生。
三、实现
1、ConditionLooper.cs
1 ///2 /// 条件循环器 3 /// 4 internal class ConditionLooper : IDisposable 5 { 6 /// 7 /// 时间间隔,默认100毫秒(以毫秒为单位) 8 /// 9 public int Interval = 100; 10 /// 11 /// 首次调用是否延迟 指定的时间间隔,默认为 12 /// 13 public bool IsFirstDelay = false; 14 /// 15 /// 是否正在运行 16 /// 17 public bool IsRunning = false; 18 /// 19 /// 超时时间(以毫秒为单位),默认为 -1 不启用 20 /// 21 public int TimeOut = -1; 22 /// 23 /// 循环执行次数,默认为 -1 不启用 24 /// 25 public int LoopTimes = -1; 26 /// 27 /// 的执行结果, 类型枚举值。 28 /// 29 public ConditionLooperResult Result = ConditionLooperResult.None; 30 /// 31 /// 周期循环的检查任务,返回 时则会终止循环检测 32 /// 33 public event Func<bool> WorkCheck; 34 /// 35 /// 结束循环调用触发的事件 36 /// 37 public event Action EndLoop; 38 protected bool _enable; 39 /// 40 /// 启用/停用 41 /// 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 /// 111 /// 定时任务 112 /// 113 private System.Threading.Timer JobTimer; 114 /// 115 /// 总耗时计时器 116 /// 117 private Stopwatch TotalTimeWatch; 118 /// 119 /// 记录调用次数 120 /// 121 private int ExecuteTime = 0; 122 /// 123 /// 是否启用异步调用 124 /// 125 private bool IsAsyncCall = false; 126 /// 127 /// 等待信号,false - 默认等待;ture - 默认放行 128 /// 129 private AutoResetEvent WaitSignal = new AutoResetEvent(false); 130 /// 131 /// 判断是否启用超时时间设置 132 /// 133 private bool IsEnableTimeOut 134 { 135 get 136 { 137 return (this.TimeOut > 0); 138 } 139 } 140 /// 141 /// 判断是否启用次数限制设置 142 /// 143 private bool IsEnableLoopTimes 144 { 145 get 146 { 147 return (this.LoopTimes > 0); 148 } 149 } 150 151 /// 152 /// 执行检查任务(同步调用) 153 /// 154 /// 155 /// 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 /// 168 /// 执行检查任务(异步调用) 169 /// 170 /// 171 /// 172 public void RunAsync(Func<bool> @checker, Action callback) 173 { 174 this.IsAsyncCall = true; 175 this.WorkCheck = @checker; 176 this.EndLoop = callback; 177 this.Enable = true; 178 } 179 /// 180 /// 释放资源 181 /// 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 /// 195 /// 停止计时器 196 /// 197 private void StopTimer() 198 { 199 if (null != this.JobTimer) 200 { 201 this.JobTimer.Dispose(); 202 this.JobTimer = null; 203 } 204 } 205 /// 206 /// 停止任务 207 /// 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 /// 227 /// 启动任务(创建并启动计时器) 228 /// 229 /// 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 /// 243 /// 重置运行状态 244 /// 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 /// 276 /// 触发结束事件 277 /// 278 /// 结果 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 /// 290 /// 记录日志 291 /// 292 /// 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 ///2 /// 循环调用结果 3 /// 4 public enum ConditionLooperResult 5 { 6 /// 7 /// 默认状态 8 /// 9 None = 0, 10 /// 11 /// 匹配 12 /// 13 Matched = 1, 14 /// 15 /// 超时 16 /// 17 TimeOut = 2, 18 /// 19 /// 超过次数限制 20 /// 21 TimesLimited = 3 22 }
3、ConditionMonitor类
1 ///2 /// 条件监视器 3 /// 4 public static class ConditionMonitor 5 { 6 /// 7 /// 周期性检测 条件表达式是否满足,直到满足条件或超时时间已到则退出。 8 /// 9 /// 条件表达式。 10 /// 检测时间间隔,默认100毫秒。 11 /// 检测超时时间,-1 为不启用, 表示的条件表达满足时才会退出。 12 /// 首次调用是否延迟 指定的时间间隔,默认为 13 /// 检测结果 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 /// 23 /// 周期性检测 条件表达式是否满足,直到满足条件或循环检测次数已到则退出。 24 /// 25 /// 条件表达式。 26 /// 检测时间间隔,默认1000毫秒即1秒。 27 /// 循环检测的次数,-1 为不启用, 表示的条件表达满足时才会退出。 28 /// 首次调用是否延迟 指定的时间间隔,默认为 29 /// 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 /// 39 /// 异步周期性检测 条件表达式是否满足,直到满足条件或超时时间已到则退出。 40 /// 41 /// 条件表达式。 42 /// 退出时的回调函数。 43 /// 检测时间间隔,默认100毫秒。 44 /// 检测超时时间,-1 为不启用, 表示的条件表达满足时才会退出。 45 /// 首次调用是否延迟 指定的时间间隔,默认为 46 /// 检测结果 47 public static void WaitAsync(Func<bool> condition, Action 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 /// 59 /// 异步周期性检测 条件表达式是否满足,直到满足条件或循环检测次数已到则退出,通过回调函数返回执行结果。 60 /// 61 /// 条件表达式。 62 /// 退出时的回调函数。 63 /// 检测时间间隔,默认1000毫秒即1秒。 64 /// 循环检测的次数,-1 为不启用, 表示的条件表达满足时才会退出。 65 /// 首次调用是否延迟 指定的时间间隔,默认为 66 /// 67 public static void WaitForTimesAsync(Func<bool> condition, Action 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 ///2 /// 判断文件是否被占用 3 /// 4 /// 5 /// 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、测试效果