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

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

自定义等待的任务类

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# 异步并发操作,只保留最后一次操作的更多相关文章

  1. GCD 之 同步 异步 并发

    1. dispatch_async(dispatch_get_global_queue(, ), ^{ // 处理耗时操作的代码块... //通知主线程刷新 dispatch_async(dispat ...

  2. Node.js的那些坑——如何让异步并发方法同步顺序执行(for循环+异步操作)

    1 前言 nodejs的回调,有时候真的是让人又爱又恨的,当需要用for循环把数据依次存入数据库,但是如果使用正常的for循环,永远都是最后一次值的记录,根本不符合要求. 解决此方案有几种,例如闭包( ...

  3. 异步并发利器:实际项目中使用CompletionService提升系统性能的一次实践

    场景 随着互联网应用的深入,很多传统行业也都需要接入到互联网.我们公司也是这样,保险核心需要和很多保险中介对接,比如阿里.京东等等.这些公司对于接口服务的性能有些比较高的要求,传统的核心无法满足要求, ...

  4. 你真的会玩SQL吗?删除重复数据且只保留一条

    在网上看过一些解决方法 我在此给出的方法适用于无唯一ID的情形 表:TB_MACVideoAndPicture 字段只有2个:mac,content mac作为ID,正常情况下mac数据是唯一的,由于 ...

  5. 【Python】迭代器、生成器、yield单线程异步并发实现详解

    转自http://blog.itpub.net/29018063/viewspace-2079767 大家在学习python开发时可能经常对迭代器.生成器.yield关键字用法有所疑惑,在这篇文章将从 ...

  6. Python 开源异步并发框架的未来

    http://segmentfault.com/a/1190000000471602 开源 Python 是开源的,介绍的这几个框架 Twisted.Tornado.Gevent 和 tulip 也都 ...

  7. Python开源异步并发框架

    Python开源异步并发框架的未来 2014年3月30日,由全球最大的中文IT社区CSDN主办的“开源技术大会·” (Open Source Technology Conference ,简称OSTC ...

  8. python异步并发模块concurrent.futures入门详解

    concurrent.futures是一个非常简单易用的库,主要用来实现多线程和多进程的异步并发. 本文主要对concurrent.futures库相关模块进行详解,并分别提供了详细的示例demo. ...

  9. SQL Server 删除重复记录,只保留一条记录

    原文地址:http://blog.csdn.net/eriato/article/details/17417303 有张表格之前没有设计关键字段的唯一约束,导致有时候执行插入操作时不小心执行了多次就出 ...

随机推荐

  1. python中PIL模块

    Image模块 Image模块是在Python PIL图像处理中常见的模块,对图像进行基础操作的功能基本都包含于此模块内.如open.save.conver.show-等功能. open类 Image ...

  2. [2018-03-06] 基于Django的轻量级CMS Mezzanine搭建笔记

    一丶什么是Mezzanine? 它是基于django的内容管理平台(),组成简单,可扩展性和定制性强,特别是个小型的个人博客系统.它也提供了类似wordpress的管理页面.博客发布.图片展示等功能, ...

  3. Nginx 的请求处理流程,你了解吗?

    之前我们已经讲解了 Nginx 的基础内容,接下来我们开始介绍 Nginx 的架构基础. 为什么我们要讨论 Nginx 的架构基础? 因为 Nginx 运行在企业内网的最外层也就是边缘节点,那么他处理 ...

  4. NOIP模拟 12

    今天过的还真是心态炸裂.. 还是莫提了吧,今日之果一定对应着今日之因. 考试前非常心虚,莫名其妙地产生了一种紧张感(????)然后果然就在T1卡题了... T1想到了减去前一项的菲波数,但是没想到交替 ...

  5. 极光推送(JPush)开篇

    Date:2019-11-11 读前思考: 极光推送是什么? 极光推送是能做什么?有什么优势? 怎么根据业务需求来实现极光推送服务呢? 简介 极光推送(JPush)是独立的第三方云推送平台,致力于为全 ...

  6. 201871010114-李岩松《面向对象程序设计(java)》第二周学习总结

    项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cnblogs.com/nwnu-daizh/p ...

  7. element 动态合并表格

    前言 element 官方的例子太简单了,不满足实际的需求 数据肯定是动态的,合并的行数,列数都是动态的,该如何知道每一行的合并数呢 需求 动态合并表格,数据来源于数据库 正文 一开始,我的数据源是单 ...

  8. 并发编程-深入浅出AQS

    AQS是并发编程中非常重要的概念,它是juc包下的许多并发工具类,如CountdownLatch,CyclicBarrier,Semaphore 和锁, 如ReentrantLock, ReaderW ...

  9. lqb 入门训练 A+B问题

    入门训练 A+B问题 时间限制:1.0s   内存限制:256.0MB     问题描述 输入A.B,输出A+B. 说明:在“问题描述”这部分,会给出试题的意思,以及所要求的目标. 输入格式 输入的第 ...

  10. nyoj 324-猴子吃桃问题 (m[i] = (m[i-1] + 1) * 2)

    324-猴子吃桃问题 内存限制:64MB 时间限制:3000ms 特判: No 通过数:20 提交数:21 难度:0 题目描述: 有一堆桃子不知数目,猴子第一天吃掉一半,又多吃了一个,第二天照此方法, ...