C# 异步并发操作,只保留最后一次操作
在我们业务操作时,难免会有多次操作,我们期望什么结果呢?
绝大部分情况,应该是只需要最后一次操作的结果,其它操作应该无效。
自定义等待的任务类
1. 可等待的任务类 AwaitableTask:
View Code
无效的操作可以分为以下俩种:
已经进行中的操作,后续结果应标记为无效
还没开始的操作,后续不执行
自定义任务类型 AwaitableTask中,添加俩个字段NotExecutable、IsInvalid:
复制代码
1 ///
2 /// 获取任务是否为不可执行状态
3 ///
4 public bool NotExecutable { get; private set; }
5 ///
6 /// 获取任务是否有效
7 /// 注:对无效任务,可以不做处理。减少并发操作导致的干扰
8 ///
9 public bool IsInvalid { get; private set; } = true;
复制代码
2. 有返回结果的可等待任务类 AwaitableTask:
View Code
添加任务等待器,同步等待结果返回:
复制代码
1 ///
2 /// 获取任务等待器
3 ///
4 ///
5 public new TaskAwaiter GetAwaiter() => new TaskAwaiter(this);
6
7 ///
8 /// 任务等待器
9 ///
10 [HostProtection(SecurityAction.LinkDemand, ExternalThreading = true, Synchronization = true)]
11 public new struct TaskAwaiter : INotifyCompletion
12 {
13 private readonly AwaitableTask _task;
14
15 ///
16 /// 初始化任务等待器
17 ///
18 ///
19 public TaskAwaiter(AwaitableTask awaitableTask) => _task = awaitableTask;
20
21 ///
22 /// 任务是否已完成。
23 ///
24 public bool IsCompleted => _task._task.IsCompleted;
25
26 ///
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 ///
37 /// 获取任务结果。
38 ///
39 ///
40 public TResult GetResult() => _task._task.Result;
41 }
复制代码
异步任务队列
添加异步任务队列类,用于任务的管理,如添加、执行、筛选等:
View Code
1. 自动取消之前的任务 AutoCancelPreviousTask
内部使用线程,循环获取当前任务列表,如果当前任务被标记NotExecutable不可执行,则跳过。
NotExecutable是何时标记的?
获取任务时,标记所有获取的任务为NotExecutable。直到任务列表中为空,那么只执行最后获取的一个任务。
2. 标记已经进行的任务无效 MarkTaskValid
当前进行的任务,无法中止,那么标记为无效即可。
复制代码
1 ///
2 /// 上一次异步操作
3 ///
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 ///
2 /// 执行异步操作
3 ///
4 /// 返回结果类型
5 /// 异步操作
6 /// isInvalid:异步操作是否有效;result:异步操作结果
7 public async Task<(bool isInvalid, T reslut)> ExecuteAsync(Func> 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次操作被取消不执行。https://www.cnblogs.com/kybs0/p/11988554.html