.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.基本概念 除了顺序执行和并行执行的模型以外,还有异步模型,这是事件驱动模型的基础.异步活动的执行模型可以只有一个单一的主控制流,能在单核心系统和多核心系统中运行. 在并发执行的异步模型中,许多任务 ...
随机推荐
- ssh(sturts2_spring_hibernate) 框架搭建之struts2
一.struts2完整流程的逻辑(整体的概述) 首先,用户在地址栏中输入你的项目访问路径,然后这个请求会发送到服务器,之后服务器发现在web.xml中配置了一个filter过滤器,并且这个过滤器需要对 ...
- SSIS的DelayValidation属性
一,DelayValidation Property true if validation of the package is delayed until run time. false if the ...
- JS 关于(function( window, undefined ) {})(window)写法的理解
JS 关于(function( window, undefined ) {})(window)写法的理解 [网络整理] (function( window, undefined ) {})(windo ...
- Android自定义ImageView圆形头像
效果图: 代码如下: RoundImageView.java import cn.comnav.evaluationsystem.R; import android.content.Context; ...
- [转载]AxureRP常用快捷键
习惯用Axure快捷键会让你做原型的时候更得心应手.Axure中文网总结了常用的一些快捷键分享给大家 . Axure RP Pro 6.5快捷键大全,如有疏漏,欢迎补充. 基本快捷键: 打开: ...
- Gradify - 提取图片颜色,创建响应式的 CSS渐变
被请求的HTTP对象之间的延迟会有一个时间段,这个期间网页看起来不完整.Gradify 可以分析出图像中4个最常见的颜色,创建一个梯度(或纯色)作为图片占位符.Gradify 可以在在任何图像发现最突 ...
- Windows Azure Cloud Service (36) 在Azure Cloud Service配置SSL证书
<Windows Azure Platform 系列文章目录> 在某些时候,我们需要在Azure PaaS Cloud Service配置HTTPS连接.本章将介绍如何在本地创建证书,然后 ...
- JavaScript之旅(三)
JavaScript之旅(三) 三.函数 在JavaScript中,定义函数的方式如下: function abs(x) { ... return ...; } 如果没有return,返回结果为und ...
- 百度Map与HT for Web结合的GIS网络拓扑应用
在<HT for Web整合OpenLayers实现GIS地图应用>篇中介绍了HT for Web与OpenLayers的整合,不少朋友反应国内用得比较多的还是百度地图,虽然HT整合百度地 ...
- HashSet 与TreeSet和LinkedHashSet的区别
Set接口 Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false. Set判断两个对象相同不是使用==运算符,而是根据equals方法.也就 ...