3层模式下长时间调用的进度条提示方案
Posted wdfrog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3层模式下长时间调用的进度条提示方案相关的知识,希望对你有一定的参考价值。
Winform + WCF +IIS 方式
任务执行时间在几分钟,需要在客户端显示进度条,与提示
原理:客户端发出任务调用请求后,服务启用一个独立线程来执行,将执行过程与结果写入一个指定内存(消息存储桶)
客户端轮询服务端的消息存储桶,来获取进度或得到结果
客户端主要代码
进度控制参数类
public class LongInvokeControl { public LongInvokeControl() { ShowPercent = true; LoopInterval = 1; } public LongInvokeControl(Object service, string sName, string lName):this() { this.Service = service; this.StartMethodName = sName; this.LoopMethodName = lName; } public String StartMethodName { get; set; } public String LoopMethodName { get; set; } public int LoopInterval { get; set; } public object Service { get; set; } public bool ShowPercent { get; set; } }
客户端调用代码,反射+泛型方法
public OutT LongInvoke<InT, OutT>(InT inParam, LongInvokeControl ctl) where OutT : class { try { using (var frm = new frmLongInvokeMonitor()) { var type = ctl.Service.GetType(); var methodStart = type.GetMethod(ctl.StartMethodName); var r1 = methodStart.Invoke(ctl.Service, new object[] { inParam }) as LongInvokeBucketInfo; var methodLoop = type.GetMethod(ctl.LoopMethodName); var loopResponse = methodLoop.Invoke(ctl.Service, new object[] { r1 }) as LongInvokeResponse<OutT>; ThreadPool.QueueUserWorkItem(o => { while (loopResponse.Status == LongInvokeStatus.Running) { #region 设置显示属性 SyncContext.Send(obj => { frm.progressBar1.Value = loopResponse.Percent ; frm.txtTips.Text = loopResponse.Tips; }, null); #endregion loopResponse = methodLoop.Invoke(ctl.Service, new object[] { r1 }) as LongInvokeResponse<OutT>; Console.WriteLine(loopResponse.Tips); Thread.Sleep(1000 * ctl.LoopInterval); } #region 关闭窗口 SyncContext.Send(obj => { frm.Close(); }, null); #endregion },null); //是否连续进度条 if (!ctl.ShowPercent) { frm.progressBar1.Style = ProgressBarStyle.Marquee; } frm.ShowDialog(); if (loopResponse.Status == LongInvokeStatus.Finish_Success) { Console.WriteLine("完成了!"); Console.WriteLine(loopResponse.Model); } else if (loopResponse.Status == LongInvokeStatus.Finish_Fail) { Console.WriteLine(loopResponse.Msg); } else { Console.WriteLine("状态异常!"); } if (loopResponse.Status != LongInvokeStatus.Finish_Success) { throw new Exception(loopResponse.Msg); } return loopResponse.Model as OutT; } } catch (Exception ex) { if (ex.InnerException != null) { throw ex.InnerException; } else { throw; } } }
使用代码
barButtonItem1.ItemClick += (s, e) => { var item = MainBindingSource.Current as LongInvokeBucketInfo; if (item == null) return; try { var service=Fetch<ILongInvokeService>(); var ret = LongInvoke<List<int>, List<String>>(new List<int>() { 11, 2, 3 }, new LongInvokeControl(service, "TestTaskStartInvoke", "TestTaskLoopInvoke") { LoopInterval = 2,ShowPercent=false }); InfoMsg("任务执行完成" + ret[0]); } catch (Exception ex) { ErrMsg(ex.Message); } return; }; barButtonItem2.ItemClick += (s, e) => { var item = MainBindingSource.Current as LongInvokeBucketInfo; if (item == null) return; try { var service = Fetch<ILongInvokeService>(); var ret = LongInvoke<List<int>, List<String>>(new List<int>() { 11, 2, 3 }, new LongInvokeControl(service,"TestTaskStartInvoke","TestTaskLoopInvoke") {LoopInterval=1 }); InfoMsg("任务执行完成" + ret[0]); } catch (Exception ex) { ErrMsg(ex.Message); } return; }; barButtonItem3.ItemClick += (s, e) => { var item = MainBindingSource.Current as LongInvokeBucketInfo; if (item == null) return; try { var service = Fetch<ILongInvokeService>(); var ret = LongInvoke<List<int>, List<String>>(new List<int>() { 101, 2, 3 }, new LongInvokeControl() { Service = service, StartMethodName = "TestTaskStartInvoke", LoopMethodName= "TestTaskLoopInvoke",ShowPercent= true }); InfoMsg("任务执行完成" + ret[0]); } catch (Exception ex) { ErrMsg(ex.Message); } return; };
服务端主代码:
消息存储桶,调用任务控制块,控制消息桶数量,也就控制了整个系统允许并发执行的总任务书
另外该类提供2个模板方法供,WCF服务调用
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections.Concurrent; using F.Studio.Prime.EFModel; using System.Threading; namespace F.Studio.Prime.Service { public class LongInvokeBucketMgr:IDisposable { private const int C_MaxSink = 50; private readonly static LongInvokeBucketMgr _Instance = new LongInvokeBucketMgr(); public List<LongInvokeBucket> BucketList = new List<LongInvokeBucket>(); private Object lockObj = new object(); public static LongInvokeBucketMgr Instance { get { return _Instance; } } private LongInvokeBucketMgr() { InitMgr(); } private void InitMgr() { for (int i = 0; i < C_MaxSink; i++) { var bucket=new LongInvokeBucket(); bucket.Id=i; bucket.Model = null; bucket.Percent = -1; bucket.Status = LongInvokeStatus.Init; bucket.Tips = ""; bucket.Msg = ""; bucket.Code = -1; bucket.Guid = ""; bucket.IsFree = true; BucketList.Add(bucket); } } public LongInvokeBucket Allocate() { lock (lockObj) { var it = BucketList.FirstOrDefault(ent => ent.IsFree == true); if (it == null) throw new Exception("没有可分配的消息存储桶!"); it.IsFree = false; it.Guid = Guid.NewGuid().ToString(); it.Status = LongInvokeStatus.Running; it.BTime = DateTime.Now; it.ETime = null; it.Code = 0; it.Percent = 0; it.Tips = ""; it.Msg = ""; it.Data = ""; it.TaskName = ""; return it; } } public void Free(int id) { lock (lockObj) { var it = BucketList.FirstOrDefault(ent => ent.Id == id); if (it == null) throw new Exception(string.Format("指定的消息存储桶编号不存在{0}", id)); it.IsFree = true; } } public List<LongInvokeBucketInfo> GetList() { var list = new List<LongInvokeBucketInfo>(); foreach (var item in BucketList) { var info = new LongInvokeBucketInfo(); SetInfo(item, info); list.Add(info); } return list; } private static void SetInfo(LongInvokeBucket item, LongInvokeBucketInfo info) { info.BTime = item.BTime; info.ETime = item.ETime; info.Code = item.Code; info.Guid = item.Guid; info.Id = item.Id; info.IsFree = item.IsFree; info.Msg = item.Msg; info.Percent = item.Percent; info.Status = item.Status; info.TaskName = item.TaskName; info.Tips = item.Tips; } public static LongInvokeBucketInfo ConvertToInfo(LongInvokeBucket from) { LongInvokeBucketInfo info = new LongInvokeBucketInfo(); SetInfo(from, info); return info; } public LongInvokeBucket QueryBucket(string guid) { var it= BucketList.FirstOrDefault(ent => ent.Guid == guid); if (it == null) throw new Exception("未找到指定的消息存储桶(运行控制块)!"); return it; } public void Stop() { BucketList.Clear(); } #region 执行与轮询模板代码 /// <summary> /// 模板代码 /// 需要提供Func方法实现,该方法输入LongInvokeBucket,返回ReturnT类型 /// </summary> /// <typeparam name="ReturnT"></typeparam> /// <param name="func"></param> /// <returns></returns> public static LongInvokeBucketInfo StartInvoke<ReturnT>(Func<LongInvokeBucket, ReturnT> func) { var bucket = LongInvokeBucketMgr.Instance.Allocate(); var taskInfo = LongInvokeBucketMgr.ConvertToInfo(bucket); var oldGuid = bucket.Guid; ThreadPool.QueueUserWorkItem(o => { try { bucket.Tips = "任务启动..."; var model = func(bucket); bucket.Tips = "完成"; bucket.Percent = 100; bucket.Code = 0; bucket.Model = model; bucket.Status = LongInvokeStatus.Finish_Success; } catch (Exception ex) { bucket.Tips = "执行错误"; bucket.Msg = ex.Message; bucket.Code = 2; bucket.Status = LongInvokeStatus.Finish_Fail; } finally { bucket.ETime = DateTime.Now; Thread.Sleep(1000 * 10); //10秒后世界依旧如故,那么就释放吧 if (bucket.Guid == oldGuid) { LongInvokeBucketMgr.Instance.Free(bucket.Id); } } }); return taskInfo; } public static LongInvokeResponse<ReturnT> LoopInvoke<ReturnT>(LongInvokeBucketInfo request) where ReturnT : class { var response = new LongInvokeResponse<ReturnT>(); try { var bucket = LongInvokeBucketMgr.Instance.QueryBucket(request.Guid); if (bucket.IsFree) throw new Exception("已经释放了消息存储桶!"); response.Tips = bucket.Tips; response.Status = bucket.Status; response.Percent = bucket.Percent; response.Code = bucket.Code; response.Msg = bucket.Msg; //立即释放 if (bucket.Status == LongInvokeStatus.Finish_Success) { response.Model = bucket.Model as ReturnT; LongInvokeBucketMgr.Instance.Free(request.Id); } else if (bucket.Status == LongInvokeStatus.Finish_Fail) { LongInvokeBucketMgr.Instance.Free(request.Id); } } catch (Exception ex) { response.Status = LongInvokeStatus.Finish_Fail; response.Code = 2; response.Msg = ex.Message; response.Tips = ex.Message; } return response; } /// <summary> /// 轮询调用,注意客户端检测到任务完成后, /// 请不要再发起调用 /// </summary> /// <typeparam name="ReturnT"></typeparam> /// <typeparam name="InT"></typeparam> /// <param name="request"></param> /// <param name="act"></param> /// <returns></returns> public static LongInvokeResponse<ReturnT> LoopInvoke<ReturnT, InT>(LongInvokeRequest<InT> request, Action<LongInvokeBucket> act) where ReturnT : class { var response = new LongInvokeResponse<ReturnT>(); try { var bucket = LongInvokeBucketMgr.Instance.QueryBucket(request.Guid); if (bucket.IsFree) throw new Exception("已经释放了消息存储桶!"); if (act != null) { act(bucket); } response.Tips = bucket.Tips; response.Status = bucket.Status; response.Percent = bucket.Percent; response.Code = bucket.Code; response.Msg = bucket.Msg; //立即释放 if (bucket.Status == LongInvokeStatus.Finish_Success) { response.Model = bucket.Model as ReturnT; LongInvokeBucketMgr.Instance.Free(request.Id); } else if (bucket.Status == LongInvokeStatus.Finish_Fail) { LongInvokeBucketMgr.Instance.Free(request.Id); } } catch (Exception ex) { response.Status = LongInvokeStatus.Finish_Fail; response.Code = 2; response.Msg = ex.Message; response.Tips = ex.Message; } return response; } #endregion #region IDisposable Members private bool disposed = false; /// <summary> /// Performs application-defined tasks associated with freeing, /// releasing, or resetting unmanaged resources. /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed /// and unmanaged resources; <c>false</c> /// to release only unmanaged resources. /// </param> protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { try { Stop(); } catch { } } disposed = true; } } #endregion } }
一个WCF服务实现类,注意为完成进度跟踪需要实现2个方法
注意下面func方法返回值是异步调用最终的返回值(看成同步调用时)
异步调用使用了ThreadPool线程,
using System; using System.Collections.Generic; using System.Linq; using System.Text; using F.Studio.Prime.IService; using F.Studio.Prime.EFModel; using System.Threading; using System.ServiceModel.Activation; using System.ServiceModel; namespace F.Studio.Prime.Service { [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)] [ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple, MaxItemsInObjectGraph = 999999999, IgnoreExtensionDataObject = true, UseSynchronizationContext = false)] public class LongInvokeService : ILongInvokeService { public List<LongInvokeBucketInfo> GetBucketList() { return LongInvokeBucketMgr.Instance.GetList(); } public int Free(int id) { LongInvokeBucketMgr.Instance.Free(id); return 0; } public LongInvokeBucketInfo TestTaskStartInvoke(List<int> list) { Func<LongInvokeBucket, List<String>> func=(bucket)=>{ #region // bucket.TaskName = "Long Invoke Test"; // for (int i = 0; i < 10; i++) // { // bucket.Tips = "执行" + i + "项操作..."; // bucket.Percent = bucket.Percent + 5; // Thread.Sleep(1000 * 1); // if (i > 5 && list[0] == 101) throw new Exception("碰到错误了,Func!"); // } // bucket.Tips = "执行数据保存操作..."; // bucket.Percent = 99; // Thread.Sleep(1000 * 3); // bucket.Tips = "任务执行完成"; //return new List<String>() {"123","abc","ABC" }; #endregion var actor= new TaskActor(bucket, list); actor.Do(); return actor.Resulte; }; return LongInvokeBucketMgr.StartInvoke(func); } public LongInvokeResponse<List<String>> TestTaskLoopInvoke(LongInvokeBucketInfo request) { var response = LongInvokeBucketMgr.LoopInvoke<List<String>>(request); return response; } public LongInvokeResponse<List<string>> LoopTestTask(LongInvokeRequest<List<int>> request) { Action<LongInvokeBucket> act = (b) => { if (request.Model.Count > 0) { if (request.Model[0] == 101) { throw new Exception("Test:第一个元素是101"); } } }; var response = LongInvokeBucketMgr.LoopInvoke<List<String>, List<int>>(request, act); return response; } public class TaskActor { private LongInvokeBucket _Bucket; private List<int> _List; public List<String> Resulte; public TaskActor(LongInvokeBucket bucket,List<int> list) { _Bucket = bucket; _List = list; } public void Do() { _Bucket.TaskName = "Long Invoke Test"; for (int i = 0; i < 10; i++) { _Bucket.Tips = "执行" + i + "项操作..."; _Bucket.Percent = _Bucket.Percent + 5; Thread.Sleep(1000 * 1); if (i > 5 && _List[0] == 101) throw new Exception("碰到错误了,Func!"); } _Bucket.Tips = "执行数据保存操作..."; _Bucket.Percent = 99; Thread.Sleep(1000 * 3); _Bucket.Tips = "任务执行完成"; Resulte = new List<String>() { "123", "abc", "ABC","EFG" }; } } } }
以上是关于3层模式下长时间调用的进度条提示方案的主要内容,如果未能解决你的问题,请参考以下文章