比尔云BierYun--阿里云最新优惠活动
阿里云优惠码丨阿里云代金券

C# 异步并发操作,只保留最后一次操作

C# 异步并发操作,只保留最后一次操作

在我们业务操作时,难免会有多次操作,我们期望什么结果呢?

绝大部分情况,应该是只需要最后一次操作的结果,其它操作应该无效。

自定义等待的任务类

1. 可等待的任务类 AwaitableTask:

 View Code

无效的操作可以分为以下俩种:

  • 已经进行中的操作,后续结果应标记为无效
  • 还没开始的操作,后续不执行

自定义任务类型 AwaitableTask中,添加俩个字段NotExecutable、IsInvalid:

复制代码
1     /// <summary>
2     /// 获取任务是否为不可执行状态
3     /// </summary>
4     public bool NotExecutable { get; private set; }
5     /// <summary>
6     /// 获取任务是否有效
7     /// 注:对无效任务,可以不做处理。减少并发操作导致的干扰
8     /// </summary>
9     public bool IsInvalid { get; private set; } = true;
复制代码

2. 有返回结果的可等待任务类 AwaitableTask<TResult>:

 View Code

添加任务等待器,同步等待结果返回:

复制代码
 1    /// <summary>
 2     /// 获取任务等待器
 3     /// </summary>
 4     /// <returns></returns>
 5     public new TaskAwaiter GetAwaiter() => new TaskAwaiter(this);
 6 
 7     /// <summary>
 8     /// 任务等待器
 9     /// </summary>
10     [HostProtection(SecurityAction.LinkDemand, ExternalThreading = true, Synchronization = true)]
11     public new struct TaskAwaiter : INotifyCompletion
12     {
13         private readonly AwaitableTask<TResult> _task;
14 
15         /// <summary>
16         /// 初始化任务等待器
17         /// </summary>
18         /// <param name="awaitableTask"></param>
19         public TaskAwaiter(AwaitableTask<TResult> awaitableTask) => _task = awaitableTask;
20 
21         /// <summary>
22         /// 任务是否已完成。
23         /// </summary>
24         public bool IsCompleted => _task._task.IsCompleted;
25 
26         /// <inheritdoc />
27         public void OnCompleted(Action continuation)
28         {
29             var This = this;
30             _task._task.ContinueWith(t =>
31             {
32                 if (!This._task.NotExecutable) continuation?.Invoke();
33             });
34         }
35 
36         /// <summary>
37         /// 获取任务结果。
38         /// </summary>
39         /// <returns></returns>
40         public TResult GetResult() => _task._task.Result;
41     }
复制代码

异步任务队列

添加异步任务队列类,用于任务的管理,如添加、执行、筛选等:

 View Code

1. 自动取消之前的任务 AutoCancelPreviousTask

内部使用线程,循环获取当前任务列表,如果当前任务被标记NotExecutable不可执行,则跳过。

NotExecutable是何时标记的?

获取任务时,标记所有获取的任务为NotExecutable。直到任务列表中为空,那么只执行最后获取的一个任务。

2. 标记已经进行的任务无效 MarkTaskValid

当前进行的任务,无法中止,那么标记为无效即可。

复制代码
 1     /// <summary>
 2     /// 上一次异步操作
 3     /// </summary>
 4     private AwaitableTask _lastDoingTask;
 5     private bool TryGetNextTask(out AwaitableTask task)
 6     {
 7         task = null;
 8         while (_queue.Count > 0)
 9         {
10             //获取并从队列中移除任务
11             if (_queue.TryDequeue(out task) && (!AutoCancelPreviousTask || _queue.Count == 0))
12             {
13                 //设置进行中的异步操作无效
14                 _lastDoingTask?.MarkTaskValid();
15                 _lastDoingTask = task;
16                 return true;
17             }
18             //并发操作,设置任务不可执行
19             task.SetNotExecutable();
20         }
21         return false;
22     }
复制代码

后续执行完后,根据此标记,设置操作结果为空。

复制代码
 1     /// <summary>
 2     /// 执行异步操作
 3     /// </summary>
 4     /// <typeparam name="T">返回结果类型</typeparam>
 5     /// <param name="func">异步操作</param>
 6     /// <returns>isInvalid:异步操作是否有效;result:异步操作结果</returns>
 7     public async Task<(bool isInvalid, T reslut)> ExecuteAsync<T>(Func<Task<T>> func)
 8     {
 9         var task = GetExecutableTask(func);
10         var result = await await task;
11         if (!task.IsInvalid)
12         {
13             result = default(T);
14         }
15         return (task.IsInvalid, result);
16     }
复制代码

实践测试

启动10个并发任务,测试实际的任务队列并发操作管理:

复制代码
 1     public MainWindow()
 2     {
 3         InitializeComponent();
 4         _asyncTaskQueue = new AsyncTaskQueue
 5         {
 6             AutoCancelPreviousTask = true,
 7             UseSingleThread = true
 8         };
 9     }
10     private AsyncTaskQueue _asyncTaskQueue;
11     private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
12     {
13         // 快速启动10个任务
14         for (var i = 1; i < 10; i++)
15         {
16             Test(_asyncTaskQueue, i);
17         }
18     }
19     public static async void Test(AsyncTaskQueue taskQueue, int num)
20     {
21         var result = await taskQueue.ExecuteAsync(async () =>
22         {
23             Debug.WriteLine("输入:" + num);
24             // 长时间耗时任务
25             await Task.Delay(TimeSpan.FromSeconds(5));
26             return num * 100;
27         });
28         Debug.WriteLine($"{num}输出的:" + result);
29     }
复制代码

测试结果如下:

只有最后一次操作结果,才是有效的。其它9次操作,一次是无效的,8次操作被取消不执行。

Demo,见Github

原文地址https://www.cnblogs.com/kybs0/p/11988554.html

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

强烈推荐

高性能SSD云服务器ECS抗攻击,高可用云数据库RDS