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; }
    }
View Code

客户端调用代码,反射+泛型方法

 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;
                }
            }
        }
View Code

使用代码

  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;
            };
View Code

服务端主代码:

消息存储桶,调用任务控制块,控制消息桶数量,也就控制了整个系统允许并发执行的总任务书

另外该类提供2个模板方法供WCF服务调用

一个WCF服务实现类,注意为完成进度跟踪需要实现2个方法

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 = "";
                it.Model = null;
                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;
                it.Model = null;
            }
        }
        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
    }
}
View Code

注意下面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" };
            }

        }
    }
}
View Code
    public class LongInvokeBucket:LongInvokeBucketInfo
    {
        public LongInvokeBucket()
        {
            IsFree = true;
        }
        public void P(String tips)
        {
            this.Tips = tips;
        }
        public void P(String tips, int percent)
        {
            this.Tips = tips;
            this.Percent = percent;
        }
        public void P(int percent)
        {
            this.Percent = percent;
        }
            
    }

    public enum LongInvokeStatus { Init=-1,Running = 1, Finish_Success = 0, Finish_Fail = 2 }

    public class LongInvokeBucketInfo
    {
        public int Id { get; set; }
        public String TaskName { get; set; }
        public String Tips { get; set; }
        public int Percent { get; set; }
        /// <summary>
        /// 结束_失败
        /// 结束_成功
        /// 执行中
        /// </summary>
        public LongInvokeStatus Status { get; set; }
        public Object Model { get; set; }
        public String Guid { get; set; }
        public DateTime? BTime { get; set; }
        public DateTime? ETime { get; set; }
        /// <summary>
        /// 结果状态 
        /// 0:成功
        /// 其他执行失败
        /// </summary>
        public int Code { get; set; }
        public String Msg { get; set; }
        public bool IsFree { get; set; }
        public String Data { get; set; }
    }
View Code
原文地址:https://www.cnblogs.com/wdfrog/p/9875314.html