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 有张表格之前没有设计关键字段的唯一约束,导致有时候执行插入操作时不小心执行了多次就出 ...
随机推荐
- [UWP]使用GetAlphaMask和ContainerVisual制作长阴影(Long Shadow)
1. 什么是长阴影 前几年扁平化设计(Flat Design)十分流行,后来在扁平化的基础上又流行起了长阴影(Long Shadow).长阴影其实就是扩展了对象的投影,感觉是一种光线照射下的影子,通常 ...
- SpringBoot系列:Spring Boot异步调用@Async
在实际开发中,有时候为了及时处理请求和进行响应,我们可能会多任务同时执行,或者先处理主任务,也就是异步调用,异步调用的实现有很多,例如多线程.定时任务.消息队列等, 这一章节,我们就来讲讲@Async ...
- 前端技术之:常见前端UI相关开源项目
Bootstrap https://getbootstrap.com/BootstrapVue provides one of the most comprehensive implementatio ...
- Apache配置反向代理、负载均衡和集群(mod_proxy方式)
Apache配置负载均衡和集群使用mod_jk的方式比较多,但是mod_jk已经停止更新,并且配置相对复杂.Apache2.2以后,提供了一种原生的方式配置负载均衡和集群,比mod_jk简单很多. 1 ...
- Apache的虚拟主机功能
Apache的虚拟主机功能 (Virtual Host) 是可以让一台服务器基于IP.主机名或端口号实现提供多个网站服务的技术. 第一种情况:基于IP地址 这种情况很常见:一台服务器拥有多个IP地址, ...
- 识别手写数字增强版100% - pytorch从入门到入道(一)
手写数字识别,神经网络领域的“hello world”例子,通过pytorch一步步构建,通过训练与调整,达到“100%”准确率 1.快速开始 1.1 定义神经网络类,继承torch.nn.Modul ...
- JAVA Rest High Level Client如何取聚合后得数据
对于刚刚学习es的童鞋来说,很容易不清楚怎么获取客户端对es文档的聚合结果,下面就演示一下模仿DSL写聚合,然后获取到聚合对结果. 一, 对于下面这个简单的聚合,目的是对于文档全文匹配,聚合颜色字段. ...
- 11、pytest -- 测试的参数化
目录 1. @pytest.mark.parametrize标记 1.1. empty_parameter_set_mark选项 1.2. 多个标记组合 1.3. 标记测试模块 2. pytest_g ...
- Python OpenCV4趣味应用系列(四)---颜色物体实时检测
今天,我们来实现一个视频实时检测颜色物体的小实例,视频中主要有三个颜色物体,我们只检测红色和绿色的球状物体,如下图所示: 第一步需要打开视频(或者摄像头): cap = cv2.VideoCaptur ...
- 理解Spark SQL(一)—— CLI和ThriftServer
Spark SQL主要提供了两个工具来访问hive中的数据,即CLI和ThriftServer.前提是需要Spark支持Hive,即编译Spark时需要带上hive和hive-thriftserver ...