.Net中的并行编程-4.实现高性能异步队列
上文《.Net中的并行编程-3.ConcurrentQueue实现与分析》分析了ConcurrentQueue的实现,本章就基于ConcurrentQueue实现一个高性能的异步队列,该队列主要用于实时数据流的处理并简化多线程编程模型。设计该队列时考虑以下几点需求(需求来自公司的一个实际项目):
1. 支持多线程入队出队,尽量简化多线程编程的复杂度。
2. 支持事件触发机制,数据入队时才进行处理而不是使用定时处理机制, 而且内部能阻塞消费者线程。
3. 出队时数据处理的顺序要保证和入队时是一致的。
4. 容错性强,可以不间断运行。
以上需求点对应的解决方案:
1.ConcurrentQueue支持多线程而且多线程环境下的性能较高,对于多线程编程模型简化可用适配器模式可将消费者线程封装到队列内部,内部采用处理事件方式处理用户的任务。
2.对于事件触发机制首先信号量不适合,因为信号量达到指定数目时会阻塞线程,所以该部分需要自己编程实现(具体参考源码)。
3.队列的特性以及保证入队和出队顺序,这里需要保证的是线程处理数据项的顺序。
4.可通过注册异常处理函数的方式解决异常的问题。
所以开发出以下代码:
public class AsynQueue<T>
{
//队列是否正在处理数据
private int isProcessing;
//有线程正在处理数据
private const int Processing = ;
//没有线程处理数据
private const int UnProcessing = ;
//队列是否可用
private volatile bool enabled = true;
private Task currentTask;
public event Action<T> ProcessItemFunction;
public event EventHandler<EventArgs<Exception>> ProcessException;
private ConcurrentQueue<T> queue; public AsynQueue()
{
queue = new ConcurrentQueue<T>();
Start();
} public int Count
{
get
{
return queue.Count;
}
} private void Start()
{
Thread process_Thread = new Thread(PorcessItem);
process_Thread.IsBackground = true;
process_Thread.Start();
} public void Enqueue(T items)
{
if (items == null)
{
throw new ArgumentException("items");
} queue.Enqueue(items);
DataAdded();
} //数据添加完成后通知消费者线程处理
private void DataAdded()
{
if (enabled)
{
if (!IsProcessingItem())
{
currentTask = Task.Factory.StartNew(ProcessItemLoop);
}
}
} //判断是否队列有线程正在处理
private bool IsProcessingItem()
{
return !(Interlocked.CompareExchange(ref isProcessing, Processing, UnProcessing) == );
} private void ProcessItemLoop()
{ if (!enabled && queue.IsEmpty)
{
Interlocked.Exchange(ref isProcessing, );
return;
}
//处理的线程数 是否小于当前最大任务数
//if (Thread.VolatileRead(ref runingCore) <= this.MaxTaskCount)
//{
T publishFrame; if (queue.TryDequeue(out publishFrame))
{ try
{
ProcessItemFunction(publishFrame);
}
catch (Exception ex)
{
OnProcessException(ex);
}
} if (enabled && !queue.IsEmpty)
{
currentTask = Task.Factory.StartNew(ProcessItemLoop);
}
else
{
Interlocked.Exchange(ref isProcessing, UnProcessing);
}
} /// <summary>
///定时处理线程调用函数
///主要是监视入队的时候线程 没有来的及处理的情况
/// </summary>
private void PorcessItem(object state)
{
int sleepCount = ;
int sleepTime = ;
while (enabled)
{
//如果队列为空则根据循环的次数确定睡眠的时间
if (queue.IsEmpty)
{
if (sleepCount == )
{
sleepTime = ;
}
else if (sleepCount <= )
{
sleepTime = * ;
}
else
{
sleepTime = * ;
}
sleepCount++;
Thread.Sleep(sleepTime);
}
else
{
//判断是否队列有线程正在处理
if (enabled && Interlocked.CompareExchange(ref isProcessing, Processing, UnProcessing) == )
{
if (!queue.IsEmpty)
{
currentTask = Task.Factory.StartNew(ProcessItemLoop);
}
else
{
Interlocked.Exchange(ref isProcessing, );
}
sleepCount = ;
sleepTime = ;
}
}
}
} public void Flsuh()
{
Stop(); if (currentTask != null)
{
currentTask.Wait();
} while (!queue.IsEmpty)
{
try
{
T publishFrame;
if (queue.TryDequeue(out publishFrame))
{
ProcessItemFunction(publishFrame);
}
}
catch (Exception ex)
{
OnProcessException(ex);
}
}
currentTask = null;
} public void Stop()
{
this.enabled = false;
} private void OnProcessException(System.Exception ex)
{
var tempException = ProcessException;
Interlocked.CompareExchange(ref ProcessException, null, null); if (tempException != null)
{
ProcessException(ex, new EventArgs<Exception>(ex));
}
}
}
[Serializable]
public class EventArgs<T> : System.EventArgs
{
public T Argument; public EventArgs() : this(default(T))
{
} public EventArgs(T argument)
{
Argument = argument;
}
}
该队列的思想是:当每次数据入队时,队列内部会调用DataAdded()判断是否数据项已经开始被处理,如果已经开始处理则数据入到内部队列后直接返回否则开启消费者线程处理。队列内部的消费者线程(线程池)(Task内部使用线程池实现,这里就当做线程池吧)会采用采用递归的方式处理数据,也就是当前数据处理完成后再将另外一个数据放到线程池去处理,这样就形成一个处理环而且保证了每条数据都有序的进行处理。由于ConcurrentQueue的IsEmpty只是当前内存的一个快照状态,可能当前时刻为空下一个时候不为空, 所以还需要一个守护线程process_Thread定时监视队列内部的消费者线程(线程池)是否正在处理数据,否则会造成消费者线程已经判断队列为空而数据已经到达只是还没插入队列此时数据可能永远得不到处理。
适用的场景:
1.适合多个生产者一个消费者的情景(当前如果需要多个消费者可以使用多个单独线程来实现)。
2.适合处理数据速度较快的情景而对于文件写入等IO操作不适合,因为线程池内部都是后台线程,当进程关闭时线程会同时关闭线程这时文件可能还没写入到磁盘。
3.适合作为流水线处理模型的基础数据结构,队列之间通过各自的事件处理函数进行通信(后续会专门撰写文章介绍关于流水线模型的应用)。
注:内部的ConcurrentQueue队列还可以使用阻塞队列(BlockingCollection)来替代,虽然使用阻塞队列更简单但是内部的消费者线程比较适合使用单独的线程不适合使用线程池,而且阻塞队列为空时会阻塞消费者线程,当然阻塞线程池内的线程也没什么影响只是不推荐这么做,而且阻塞的队列的性能也没有ConcurrentQueue的性能高。
.Net中的并行编程-4.实现高性能异步队列的更多相关文章
- .Net中的并行编程-6.常用优化策略
本文是.Net中的并行编程第六篇,今天就介绍一些我在实际项目中的一些常用优化策略. 一.避免线程之间共享数据 避免线程之间共享数据主要是因为锁的问题,无论什么粒度的锁 ...
- .Net中的并行编程-5.流水线模型实战
自己在Excel整理了很多想写的话题,但苦于最近比较忙(其实这是借口).... 上篇文章<.Net中的并行编程-4.实现高性能异步队列>介绍了异步队列的实现,本篇文章介绍我实际工作者遇到了 ...
- .Net中的并行编程-1.路线图(转)
大神,大神,膜拜膜拜,原文地址:http://www.cnblogs.com/zw369/p/3834559.html 目录 .Net中的并行编程-1.路线图 分析.Net里线程同步机制 .Net中的 ...
- .Net中的并行编程-2.ConcurrentStack的实现与分析
在上篇文章<.net中的并行编程-1.基础知识>中列出了在.net进行多核或并行编程中需要的基础知识,今天就来分析在基础知识树中一个比较简单常用的并发数据结构--.net类库中无锁栈的实现 ...
- .Net中的并行编程-3.ConcurrentQueue实现与分析
在上文<.Net中的并行编程-2.ConcurrentQueue的实现与分析> 中解释了无锁的相关概念,无独有偶BCL提供的ConcurrentQueue也是基于原子操作实现, 由于Con ...
- Python中的并行编程速度
这里主要想记录下今天碰到的一个小知识点:Python中的并行编程速率如何? 我想把AutoTool做一个并行化改造,主要目的当然是想提高多任务的执行速度.第一反应就是想到用多线程执行不同模块任务,但是 ...
- .Net中的并行编程-7.基于BlockingCollection实现高性能异步队列
三年前写过基于ConcurrentQueue的异步队列,今天在整理代码的时候发现当时另外一种实现方式-使用BlockingCollection实现,这种方式目前依然在实际项目中使用.关于Blockin ...
- .Net中的并行编程-1.路线图
最近半年一直研究用.net进行并行程序的开发与设计,再研究的过程中颇有收获,所以画了一个图总结了一下并行编程的基础知识点,这些知识点是并行编程的基础,有助于我们编程高性能的程序,里面的某些结构实现机制 ...
- Python并行编程(十四):异步编程
1.基本概念 除了顺序执行和并行执行的模型以外,还有异步模型,这是事件驱动模型的基础.异步活动的执行模型可以只有一个单一的主控制流,能在单核心系统和多核心系统中运行. 在并发执行的异步模型中,许多任务 ...
随机推荐
- KnockoutJS 3.X API 第四章(14) 绑定语法细节
data-bind绑定语法 Knockout的声明性绑定系统提供了一种简洁而强大的方法来将数据链接到UI. 绑定到简单的数据属性或使用单个绑定通常是容易和明显的. 对于更复杂的绑定,它有助于更好地了解 ...
- oracle11g AUD$维护
SYSTEM表空间使用率达到了85%,查出是用来记录审计记录的aud$表占用了很大的空间. 备份后truncate掉AUD$,问题临时解决.记得oracle11.2可以把aud$迁移到普通的表空 间. ...
- Web应用安全之文件上传漏洞详解
什么是文件上传漏洞 文件上传漏洞是在用户上传了一个可执行的脚本文件,本通过此脚本文件获得了执行服务器端命令的功能,这种攻击方式是最为直接,最为有效的,有时候,几乎没有什么门槛,也就是任何人都可以进行这 ...
- ISO语言代码
// Language ISO Code Abkhazian ab Afar aa Afrikaans af Albanian sq Amharic am Arabic ar Armenian hy ...
- 【原创】Matlab.NET混合编程技巧之直接调用Matlab内置函数
本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新 Matlab和C#混合编程文章目录 :[目录]Matlab和C#混合编程文章目录 在我的上一篇文章[ ...
- Win7搭建NodeJs开发环境以及HelloWorld展示—图解
Windows 7系统下搭建NodeJs开发环境(NodeJs+WebStrom)以及Hello World!展示,大体思路如下:第一步:安装NodeJs运行环境.第二步:安装WebStrom开发工具 ...
- 拓扑排序(二)之 C++详解
本章是通过C++实现拓扑排序. 目录 1. 拓扑排序介绍 2. 拓扑排序的算法图解 3. 拓扑排序的代码说明 4. 拓扑排序的完整源码和测试程序 转载请注明出处:http://www.cnblogs. ...
- LeetCode:Maximum Depth of Binary Tree_104
LeetCode:Maximum Depth of Binary Tree [问题再现] Given a binary tree, find its maximum depth. The maximu ...
- 推荐几款制作网页滚动动画的 JavaScript 库
这里集合了几款很棒的制作网页滚动动画的 JavaScript 库和插件.它们中,有的可以帮助你在页面滚动的时候添加动感的元素动画,有的则是实现目前非常流行的全屏页面切换动画.相信借助这些插件,你也可以 ...
- Pure – 赞!轻量的、响应式的 CSS 模块集
Pure 是一组轻量的,响应式的 CSS 模块,您可以使用在任何的 Web 项目中.充分考虑了移动设备中的使用,保持文件体积尽量小,每行 CSS 都进行了仔细的考虑. Pure 基于 Normaliz ...