C# 异步并发操作,只保留最后一次操作
在我们业务操作时,难免会有多次操作,我们期望什么结果呢?
绝大部分情况,应该是只需要最后一次操作的结果,其它操作应该无效。
自定义等待的任务类
1. 可等待的任务类 AwaitableTask:
/// <summary>
/// 可等待的任务
/// </summary>
public class AwaitableTask
{
/// <summary>
/// 获取任务是否为不可执行状态
/// </summary>
public bool NotExecutable { get; private set; } /// <summary>
/// 设置任务不可执行
/// </summary>
public void SetNotExecutable()
{
NotExecutable = true;
} /// <summary>
/// 获取任务是否有效
/// 注:对无效任务,可以不做处理。减少并发操作导致的干扰
/// </summary>
public bool IsInvalid { get; private set; } = true; /// <summary>
/// 标记任务无效
/// </summary>
public void MarkTaskValid()
{
IsInvalid = false;
} #region Task private readonly Task _task;
/// <summary>
/// 初始化可等待的任务。
/// </summary>
/// <param name="task"></param>
public AwaitableTask(Task task) => _task = task; /// <summary>
/// 获取任务是否已完成
/// </summary>
public bool IsCompleted => _task.IsCompleted; /// <summary>
/// 任务的Id
/// </summary>
public int TaskId => _task.Id; /// <summary>
/// 开始任务
/// </summary>
public void Start() => _task.Start(); /// <summary>
/// 同步执行开始任务
/// </summary>
public void RunSynchronously() => _task.RunSynchronously(); #endregion #region TaskAwaiter /// <summary>
/// 获取任务等待器
/// </summary>
/// <returns></returns>
public TaskAwaiter GetAwaiter() => new TaskAwaiter(this); /// <summary>Provides an object that waits for the completion of an asynchronous task. </summary>
[HostProtection(SecurityAction.LinkDemand, ExternalThreading = true, Synchronization = true)]
public struct TaskAwaiter : INotifyCompletion
{
private readonly AwaitableTask _task; /// <summary>
/// 任务等待器
/// </summary>
/// <param name="awaitableTask"></param>
public TaskAwaiter(AwaitableTask awaitableTask) => _task = awaitableTask; /// <summary>
/// 任务是否完成.
/// </summary>
public bool IsCompleted => _task._task.IsCompleted; /// <inheritdoc />
public void OnCompleted(Action continuation)
{
var This = this;
_task._task.ContinueWith(t =>
{
if (!This._task.NotExecutable) continuation?.Invoke();
});
}
/// <summary>
/// 获取任务结果
/// </summary>
public void GetResult() => _task._task.Wait();
} #endregion }
无效的操作可以分为以下俩种:
- 已经进行中的操作,后续结果应标记为无效
- 还没开始的操作,后续不执行
自定义任务类型 AwaitableTask中,添加俩个字段NotExecutable、IsInvalid:
/// <summary>
/// 获取任务是否为不可执行状态
/// </summary>
public bool NotExecutable { get; private set; }
/// <summary>
/// 获取任务是否有效
/// 注:对无效任务,可以不做处理。减少并发操作导致的干扰
/// </summary>
public bool IsInvalid { get; private set; } = true;
2. 有返回结果的可等待任务类 AwaitableTask<TResult>:
/// <summary>
/// 可等待的任务
/// </summary>
/// <typeparam name="TResult"></typeparam>
public class AwaitableTask<TResult> : AwaitableTask
{
private readonly Task<TResult> _task;
/// <summary>
/// 初始化可等待的任务
/// </summary>
/// <param name="task">需要执行的任务</param>
public AwaitableTask(Task<TResult> task) : base(task) => _task = task; #region TaskAwaiter /// <summary>
/// 获取任务等待器
/// </summary>
/// <returns></returns>
public new TaskAwaiter GetAwaiter() => new TaskAwaiter(this); /// <summary>
/// 任务等待器
/// </summary>
[HostProtection(SecurityAction.LinkDemand, ExternalThreading = true, Synchronization = true)]
public new struct TaskAwaiter : INotifyCompletion
{
private readonly AwaitableTask<TResult> _task; /// <summary>
/// 初始化任务等待器
/// </summary>
/// <param name="awaitableTask"></param>
public TaskAwaiter(AwaitableTask<TResult> awaitableTask) => _task = awaitableTask; /// <summary>
/// 任务是否已完成。
/// </summary>
public bool IsCompleted => _task._task.IsCompleted; /// <inheritdoc />
public void OnCompleted(Action continuation)
{
var This = this;
_task._task.ContinueWith(t =>
{
if (!This._task.NotExecutable) continuation?.Invoke();
});
} /// <summary>
/// 获取任务结果。
/// </summary>
/// <returns></returns>
public TResult GetResult() => _task._task.Result;
} #endregion
}
添加任务等待器,同步等待结果返回:
/// <summary>
/// 获取任务等待器
/// </summary>
/// <returns></returns>
public new TaskAwaiter GetAwaiter() => new TaskAwaiter(this); /// <summary>
/// 任务等待器
/// </summary>
[HostProtection(SecurityAction.LinkDemand, ExternalThreading = true, Synchronization = true)]
public new struct TaskAwaiter : INotifyCompletion
{
private readonly AwaitableTask<TResult> _task; /// <summary>
/// 初始化任务等待器
/// </summary>
/// <param name="awaitableTask"></param>
public TaskAwaiter(AwaitableTask<TResult> awaitableTask) => _task = awaitableTask; /// <summary>
/// 任务是否已完成。
/// </summary>
public bool IsCompleted => _task._task.IsCompleted; /// <inheritdoc />
public void OnCompleted(Action continuation)
{
var This = this;
_task._task.ContinueWith(t =>
{
if (!This._task.NotExecutable) continuation?.Invoke();
});
} /// <summary>
/// 获取任务结果。
/// </summary>
/// <returns></returns>
public TResult GetResult() => _task._task.Result;
}
异步任务队列
/// <summary>
/// 异步任务队列
/// </summary>
public class AsyncTaskQueue : IDisposable
{
/// <summary>
/// 异步任务队列
/// </summary>
public AsyncTaskQueue()
{
_autoResetEvent = new AutoResetEvent(false);
_thread = new Thread(InternalRunning) { IsBackground = true };
_thread.Start();
} #region 执行 /// <summary>
/// 执行异步操作
/// </summary>
/// <typeparam name="T">返回结果类型</typeparam>
/// <param name="func">异步操作</param>
/// <returns>isInvalid:异步操作是否有效;result:异步操作结果</returns>
public async Task<(bool isInvalid, T reslut)> ExecuteAsync<T>(Func<Task<T>> func)
{
var task = GetExecutableTask(func);
var result = await await task;
if (!task.IsInvalid)
{
result = default(T);
}
return (task.IsInvalid, result);
} /// <summary>
/// 执行异步操作
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
public async Task<bool> ExecuteAsync<T>(Func<Task> func)
{
var task = GetExecutableTask(func);
await await task;
return task.IsInvalid;
} #endregion #region 添加任务 /// <summary>
/// 获取待执行任务
/// </summary>
/// <param name="action"></param>
/// <returns></returns>
private AwaitableTask GetExecutableTask(Action action)
{
var awaitableTask = new AwaitableTask(new Task(action));
AddPenddingTaskToQueue(awaitableTask);
return awaitableTask;
} /// <summary>
/// 获取待执行任务
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="function"></param>
/// <returns></returns>
private AwaitableTask<TResult> GetExecutableTask<TResult>(Func<TResult> function)
{
var awaitableTask = new AwaitableTask<TResult>(new Task<TResult>(function));
AddPenddingTaskToQueue(awaitableTask);
return awaitableTask;
} /// <summary>
/// 添加待执行任务到队列
/// </summary>
/// <param name="task"></param>
/// <returns></returns>
private void AddPenddingTaskToQueue(AwaitableTask task)
{
//添加队列,加锁。
lock (_queue)
{
_queue.Enqueue(task);
//开始执行任务
_autoResetEvent.Set();
}
} #endregion #region 内部运行 private void InternalRunning()
{
while (!_isDisposed)
{
if (_queue.Count == )
{
//等待后续任务
_autoResetEvent.WaitOne();
}
while (TryGetNextTask(out var task))
{
//如已从队列中删除
if (task.NotExecutable) continue; if (UseSingleThread)
{
task.RunSynchronously();
}
else
{
task.Start();
}
}
}
}
/// <summary>
/// 上一次异步操作
/// </summary>
private AwaitableTask _lastDoingTask;
private bool TryGetNextTask(out AwaitableTask task)
{
task = null;
while (_queue.Count > )
{
//获取并从队列中移除任务
if (_queue.TryDequeue(out task) && (!AutoCancelPreviousTask || _queue.Count == ))
{
//设置进行中的异步操作无效
_lastDoingTask?.MarkTaskValid();
_lastDoingTask = task;
return true;
}
//并发操作,设置任务不可执行
task.SetNotExecutable();
}
return false;
} #endregion #region dispose /// <inheritdoc />
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
} /// <summary>
/// 析构任务队列
/// </summary>
~AsyncTaskQueue() => Dispose(false); private void Dispose(bool disposing)
{
if (_isDisposed) return;
if (disposing)
{
_autoResetEvent.Dispose();
}
_thread = null;
_autoResetEvent = null;
_isDisposed = true;
} #endregion #region 属性及字段 /// <summary>
/// 是否使用单线程完成任务.
/// </summary>
public bool UseSingleThread { get; set; } = true; /// <summary>
/// 自动取消以前的任务。
/// </summary>
public bool AutoCancelPreviousTask { get; set; } = false; private bool _isDisposed;
private readonly ConcurrentQueue<AwaitableTask> _queue = new ConcurrentQueue<AwaitableTask>();
private Thread _thread;
private AutoResetEvent _autoResetEvent; #endregion
添加异步任务队列类,用于任务的管理,如添加、执行、筛选等:
1. 自动取消之前的任务 AutoCancelPreviousTask
内部使用线程,循环获取当前任务列表,如果当前任务被标记NotExecutable不可执行,则跳过。
NotExecutable是何时标记的?
获取任务时,标记所有获取的任务为NotExecutable。直到任务列表中为空,那么只执行最后获取的一个任务。
2. 标记已经进行的任务无效 MarkTaskValid
当前进行的任务,无法中止,那么标记为无效即可。
/// <summary>
/// 上一次异步操作
/// </summary>
private AwaitableTask _lastDoingTask;
private bool TryGetNextTask(out AwaitableTask task)
{
task = null;
while (_queue.Count > )
{
//获取并从队列中移除任务
if (_queue.TryDequeue(out task) && (!AutoCancelPreviousTask || _queue.Count == ))
{
//设置进行中的异步操作无效
_lastDoingTask?.MarkTaskValid();
_lastDoingTask = task;
return true;
}
//并发操作,设置任务不可执行
task.SetNotExecutable();
}
return false;
}
后续执行完后,根据此标记,设置操作结果为空。
/// <summary>
/// 执行异步操作
/// </summary>
/// <typeparam name="T">返回结果类型</typeparam>
/// <param name="func">异步操作</param>
/// <returns>isInvalid:异步操作是否有效;result:异步操作结果</returns>
public async Task<(bool isInvalid, T reslut)> ExecuteAsync<T>(Func<Task<T>> func)
{
var task = GetExecutableTask(func);
var result = await await task;
if (!task.IsInvalid)
{
result = default(T);
}
return (task.IsInvalid, result);
}
实践测试
启动10个并发任务,测试实际的任务队列并发操作管理:
public MainWindow()
{
InitializeComponent();
_asyncTaskQueue = new AsyncTaskQueue
{
AutoCancelPreviousTask = true,
UseSingleThread = true
};
}
private AsyncTaskQueue _asyncTaskQueue;
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
// 快速启动9个任务
for (var i = ; i < ; i++)
{
Test(_asyncTaskQueue, i);
}
}
public static async void Test(AsyncTaskQueue taskQueue, int num)
{
var result = await taskQueue.ExecuteAsync(async () =>
{
Debug.WriteLine("输入:" + num);
// 长时间耗时任务
await Task.Delay(TimeSpan.FromSeconds());
return num * ;
});
Debug.WriteLine($"{num}输出的:" + result);
}
测试结果如下:

一共9次操作,只有最后一次操作结果,才是有效的。其它8次操作,一次是无效的,7次操作被取消不执行。
C# 异步并发操作,只保留最后一次操作的更多相关文章
- GCD 之 同步 异步 并发
1. dispatch_async(dispatch_get_global_queue(, ), ^{ // 处理耗时操作的代码块... //通知主线程刷新 dispatch_async(dispat ...
- Node.js的那些坑——如何让异步并发方法同步顺序执行(for循环+异步操作)
1 前言 nodejs的回调,有时候真的是让人又爱又恨的,当需要用for循环把数据依次存入数据库,但是如果使用正常的for循环,永远都是最后一次值的记录,根本不符合要求. 解决此方案有几种,例如闭包( ...
- 异步并发利器:实际项目中使用CompletionService提升系统性能的一次实践
场景 随着互联网应用的深入,很多传统行业也都需要接入到互联网.我们公司也是这样,保险核心需要和很多保险中介对接,比如阿里.京东等等.这些公司对于接口服务的性能有些比较高的要求,传统的核心无法满足要求, ...
- 你真的会玩SQL吗?删除重复数据且只保留一条
在网上看过一些解决方法 我在此给出的方法适用于无唯一ID的情形 表:TB_MACVideoAndPicture 字段只有2个:mac,content mac作为ID,正常情况下mac数据是唯一的,由于 ...
- 【Python】迭代器、生成器、yield单线程异步并发实现详解
转自http://blog.itpub.net/29018063/viewspace-2079767 大家在学习python开发时可能经常对迭代器.生成器.yield关键字用法有所疑惑,在这篇文章将从 ...
- Python 开源异步并发框架的未来
http://segmentfault.com/a/1190000000471602 开源 Python 是开源的,介绍的这几个框架 Twisted.Tornado.Gevent 和 tulip 也都 ...
- Python开源异步并发框架
Python开源异步并发框架的未来 2014年3月30日,由全球最大的中文IT社区CSDN主办的“开源技术大会·” (Open Source Technology Conference ,简称OSTC ...
- python异步并发模块concurrent.futures入门详解
concurrent.futures是一个非常简单易用的库,主要用来实现多线程和多进程的异步并发. 本文主要对concurrent.futures库相关模块进行详解,并分别提供了详细的示例demo. ...
- SQL Server 删除重复记录,只保留一条记录
原文地址:http://blog.csdn.net/eriato/article/details/17417303 有张表格之前没有设计关键字段的唯一约束,导致有时候执行插入操作时不小心执行了多次就出 ...
随机推荐
- MacOS 系统 文件夹解析
Mac OS X,基于UNIX核心的系统,增强了系统的稳定性.性能以及响应能力. 通过对称多处理技术充分发挥双处理器的优势,提供无与伦比的2D.3D和多媒体图形性能以及广泛的字体支持和集成的PDA功能 ...
- PowerBI开发 第十六篇:PowerBI Service基本概念
从总体上来看,PowerBI Service 有4个主要的构建模块,分别是dashboards.reports.workbooks 和 datasets,这四个模块都是目录,位于workspaces目 ...
- git .gitignore详解
1.最近使用git又遇到一个陷阱: 场景:A和B使用的不同的编译器做的同一个解决方案下的不同的项目工程,刚开始没考虑到版本问题,后来发现A上传的csproj在B需要做很麻烦修改才能打开,后来想到各自用 ...
- m102 SE赛
这次考试考完试正在刷提交记录的时候,到我这突然oj卡了一下,然后卡了大约10s,再刷出来就发现:
- jquery鼠标点击穿透的解决方法
jquery鼠标点击穿透的解决方法 <pre><div class="showcontainer" style="background:#000;dis ...
- 《计算机网络 自顶向下方法》 第2章 应用层 Part2
域名.主机名? 从范围上看: 域名的范围比主机名大 一个域名下通常有多个主机名 从组成上看: 主机名 = 服务器名(或计算机名) + 域名 举例说明: baidu.com 是百度的域名 www.b ...
- 装上linux后的准备工作
A.修改对应网卡的IP地址的配置文件 1 2 3 4 5 6 7 8 # vi /etc/sysconfig/network-scripts/ifcfg-eth0 IPV6INIT=no #关闭 ...
- 生信 - 从repeatmasker传送门过来的 blast
以前有的是非完整时间写的博客,抽时间需要统一整理一下. 今天在重新装repeatmasker. 整个过程是这样的,有关联的事情有两个. 1. 装repeatmasker需要各种Prerequisite ...
- LyX Error convert to loadable format - error handling
This question used to spend my half a day, and this time again, half a day. Here I write it down in ...
- nyoj 845-无主之地1 (struct)
845-无主之地1 内存限制:64MB 时间限制:1000ms 特判: No 通过数:8 提交数:16 难度:0 题目描述: 子晓最近在玩无主之地1,他对这个游戏的评价不错,结合了FPS与RPG元素, ...