利用异步实现进度
前些时间去面试,遇到面试官问我在一个任务中实现进度效果。
可能一部分人想到的就是文件上传下载利用js的插件就可以实现了,cs模式也是有相应的插件的去实现,但实际上需求是希望实现某项操作,比如大型数据的计算,可能不是一时半会能完成的,也不能让客户分步骤去完成。
首先我们要将任务分段,如果是有序的任务,比较好处理,比如批量对某些数据进行操作,那么可以根据数据的条数为总量除以完成数量得到任务执行百分比,如果是无序的,就只能对任务进行分段,根据每段的执行时间做一个任务占比的处理。
而如何获取这些完成的百分比,有两种方式,一种是利用websocket或者SignalR回传完成的百分比,这个是同步的情况。
另外一种可以利用异步去实现,这是接下来要介绍的。
首先我们要创建一个异步线程任务:(ps:SmartThreadPool 智能线程池,可以在NuGet查询添加)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Amib.Threading; namespace Task { public interface IAsyncTask { AsyncTaskProgress AsyncTaskProgress { get; set; } WorkItemPriority Priority { get; set; } string TaskKey { get; set; } bool IsAuto { get; set; } void Queue(); void Execute(); void Prepareing(); void Processing(); void Exit(); void ReStart(); void Cancel(); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Task { public class AsyncTaskProgress { public AsyncTaskProgress() { } public AsyncTaskProgress(bool isPercentTask, decimal percent, string message) { IsPercentTask = isPercentTask; Percent = percent; Message = message; } public bool IsPercentTask { get; set; } public decimal Percent { get; set; } public string Message { get; set; } public AsyncTaskProgressState State { get; set; } public bool IsFinish { get { return State == AsyncTaskProgressState.Success || State == AsyncTaskProgressState.Cancel || State == AsyncTaskProgressState.Error; } } } }
将这个调度任务装进异步线程任务池里面,
using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Text; using System.Threading; using Amib.Threading; namespace Task { /// <summary> /// 创建线程池 /// </summary> public class AsyncTaskPool { public static Dictionary<string, IAsyncTask> Tasks { get; set; } public static SmartThreadPool SmartThreadPool { get; set; } public static object Object = new object(); static AsyncTaskPool() { Tasks = new Dictionary<string, IAsyncTask>(); SmartThreadPool = new SmartThreadPool(); } public static IWorkItemResult AddTask(IAsyncTask task) { lock (Object) { if (Tasks.ContainsKey(task.TaskKey)) { if (Tasks[task.TaskKey].AsyncTaskProgress.IsFinish) { Tasks.Remove(task.TaskKey); } else { throw new Exception("该任务已存在!"); } } Tasks.Add(task.TaskKey, task); return SmartThreadPool.QueueWorkItem(task.Execute, task.Priority); } } public static IAsyncTask GeTask(string key) { if (Tasks.ContainsKey(key)) { return Tasks[key]; } else { throw new Exception("该任务不存在!"); } } public static bool Remove(string key) { if (Tasks.ContainsKey(key)) { return Tasks.Remove(key); } return true; } public static bool Cancel(string key) { if (Tasks.ContainsKey(key)) { Tasks[key].Cancel(); } return Remove(key); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Amib.Threading; namespace Task { public class BaseAsyncTask : IAsyncTask { public BaseAsyncTask(string key, bool isAuto, WorkItemPriority priority = WorkItemPriority.Normal, bool isAutoRecovery = false) { TaskKey = key; IsAuto = isAuto; Priority = priority; IsAutoRecovery = isAutoRecovery; } public AsyncTaskProgress AsyncTaskProgress { get; set; } public WorkItemPriority Priority { get; set; } /// <summary> /// 是否在异常的情况下自动重启 /// </summary> public bool IsAutoRecovery { get; set; } public string TaskKey { get; set; } public bool IsAuto { get; set; } public void Queue() { AsyncTaskPool.AddTask(this); } public virtual void Execute() { try { Prepareing(); Processing(); } catch (Exception) { //日志记录异常 throw; } finally { if (IsAuto) Exit(); //这里不能释放,不然100%的时候无法捕捉,需要捕捉到100%的时候去释放 } } public void Prepareing() { Console.WriteLine("开始执行任务..."); } public virtual void Processing() { } public void Exit() { AsyncTaskPool.Remove(this.TaskKey); } public void ReStart() { AsyncTaskPool.Cancel(this.TaskKey); AsyncTaskPool.AddTask(this); } public virtual void Cancel() { AsyncTaskPool.Cancel(this.TaskKey); } } }
简单的调用
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Remoting.Channels; using System.Text; using System.Threading; using Amib.Threading; namespace Tack { public class DalculateMaxDataTask : BaseAsyncTask { public DalculateMaxDataTask(string key, bool isAuto, WorkItemPriority priority = WorkItemPriority.Normal, bool isAutoRecovery = false) : base(key, isAuto, priority, false) { AsyncTaskProgress = new AsyncTaskProgress(); } public override void Processing() { AsyncTaskProgress.Percent = 0; Thread.Sleep(2000); AsyncTaskProgress.Percent = 10; Thread.Sleep(2000); AsyncTaskProgress.Percent = 20; Thread.Sleep(2000); AsyncTaskProgress.Percent = 30; Thread.Sleep(2000); AsyncTaskProgress.Percent = 100; } } }
实际调用以及结果