之前其他博主那里看到的例子,很值得参考
/// <summary>
/// 队列处理
/// <para>适合多个生产者一个消费者的情景</para>
/// </summary>
public class AsyncQueue<T>
{
#region 字段、属性
//有线程正在处理数据
private const int Processing = 1;
//没有线程处理数据
private const int UnProcessing = 0;
//队列是否正在处理数据
private int _isProcessing;
//队列是否可用
private volatile bool _enabled = true;
//当前任务
private Task _currentTask;
//队列
private readonly ConcurrentQueue<T> _queue;
#endregion
#region 事件
public event Action<T> ProcessItemFunction;
public event EventHandler<EventArgs> ProcessException;
#endregion
#region 构造
//public static readonly AsyncQueue<T> Instance = new Lazy<AsyncQueue<T>>(() => new AsyncQueue<T>()).Value;
/// <summary>
/// 实例化
/// </summary>
/// <param name="isRunBackgroundTask">开始一个线程防止遗漏</param>
public AsyncQueue(bool isRunBackgroundTask = false)
{
_queue = new ConcurrentQueue<T>();
if (isRunBackgroundTask)
{
Start();
}
}
#endregion
#region 操作
/// <summary>
/// 开始
/// </summary>
private void Start()
{
var processThread = new Thread(PorcessItem) {IsBackground = true};
processThread.Start();
}
/// <summary>
/// 暂停
/// </summary>
public void Stop()
{
_enabled = false;
}
/// <summary>
/// 消息入队
/// </summary>
/// <param name="item"></param>
public void Enqueue(T item)
{
if (item == null) return;
_queue.Enqueue(item);
DataAdded();
}
/// <summary>
/// 数据添加完成后通知消费者线程处理
/// </summary>
private void DataAdded()
{
if (_enabled)
{
if (!IsProcessingItem())
{
_currentTask = Task.Factory.StartNew(ProcessItemLoop);
}
}
}
/// <summary>
/// 判断是否队列有线程正在处理
/// </summary>
/// <returns></returns>
private bool IsProcessingItem()
{
return Interlocked.CompareExchange(ref _isProcessing, Processing, UnProcessing) != 0;
}
/// <summary>
/// 循环出队
/// </summary>
private void ProcessItemLoop()
{
if (!_enabled && _queue.IsEmpty)
{
Interlocked.Exchange(ref _isProcessing, 0);
return;
}
if (_queue.TryDequeue(out var 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)
{
var sleepCount = 0;
var sleepTime = 1000;
while (_enabled)
{
//如果队列为空则根据循环的次数确定睡眠的时间
if (_queue.IsEmpty)
{
if (sleepCount == 0)
{
sleepTime = 1000;
sleepCount++;
}
else if (sleepCount <= 3)
{
sleepTime = 1000 * 3;
sleepCount++;
}
else
{
sleepTime = 1000 * 60 * 5;
}
Thread.Sleep(sleepTime);
}
else
{
//判断是否队列有线程正在处理
if (_enabled && !IsProcessingItem())
{
if (!_queue.IsEmpty)
{
_currentTask = Task.Factory.StartNew(ProcessItemLoop);
}
else
{
Interlocked.Exchange(ref _isProcessing, 0);
}
sleepCount = 0;
}
}
}
}
/// <summary>
/// 完成任务后退出
/// </summary>
public void Finish()
{
Stop();
_currentTask?.Wait();
while (!_queue.IsEmpty)
{
try
{
if (_queue.TryDequeue(out var publishFrame))
{
ProcessItemFunction(publishFrame);
}
}
catch (Exception ex)
{
OnProcessException(ex);
}
}
_currentTask = null;
}
/// <summary>
/// 异常处理
/// </summary>
/// <param name="ex"></param>
private void OnProcessException(Exception ex)
{
var tempException = ProcessException;
Interlocked.CompareExchange(ref ProcessException, null, null);
if (tempException != null)
{
ProcessException(ex, new EventArgs<Exception>(ex));
}
}
#endregion
}
[Serializable]
public class EventArgs<T> : EventArgs
{
public T Argument;
public EventArgs() : this(default(T))
{
}
public EventArgs(T argument)
{
Argument = argument;
}
}
备注:忘记是那位博主写的和原文地址,知道的网友可以留言回复,我添加下转载地址
进阶版
上面的例子是适合我们理解ActionBlock对象的内部实现
// ActionBlock版,可以设置并行多少个线程,默认是单线程
ActionBlock<Model> _actionBlock = new ActionBlock<Model>(async (obj) =>
{
// DOTO:处理逻辑
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 3 });
// 入队
_actionBlock.Post(Model);
下面是参考上面例子结合ActionBlock、BatchBlock写的队列基类,适合批量操作数据存储的例子
public class MemoryQueueBase<T> where T : class, new()
{
private BatchBlock<T> _saveBatchBlock;
private const int Processing = 1; //正在处理
private const int UnProcessing = 0; //没有处理
private int _isProcessing; //是否正在处理
private int _triggerSpanTime = 0; // 触发生效时间
public event Action<IEnumerable<T>> ProcessItemFunction; //处理方法
public event EventHandler<EventArgs> ProcessException; //异常处理方法
/// <summary>
/// 初始化队列
/// </summary>
/// <param name="triggerSpanTime">多少毫秒后触发处理</param>
/// <param name="batchCount">合并多少数据后处理</param>
public MemoryQueueBase(int triggerSpanTime, int batchCount)
{
var saveActionBlock = new ActionBlock<T[]>(entityArray =>
{
try
{
ProcessItemFunction(entityArray.ToList());
}
catch (Exception ex)
{
OnProcessException(ex, entityArray);
}
});
_triggerSpanTime = triggerSpanTime < 500 ? 500 : triggerSpanTime;
// 合并N条后处理
_saveBatchBlock = new BatchBlock<T>(batchCount);
_saveBatchBlock.LinkTo(saveActionBlock);
}
/// <summary>
/// 异常处理
/// </summary>
/// <param name="ex"></param>
private void OnProcessException(Exception ex, T[] array)
{
var tempException = ProcessException;
Interlocked.CompareExchange(ref ProcessException, null, null);
if (tempException != null)
{
ProcessException(ex, new EventArgs<IEnumerable<T>>(array.ToList()));
}
}
/// <summary>
/// N秒后处理BatchBlock对象的消息数据
/// </summary>
private void TriggerBatch()
{
if (Interlocked.CompareExchange(ref _isProcessing, Processing, UnProcessing) == UnProcessing)
{
Task.Factory.StartNew(() =>
{
SpinWait.SpinUntil(() => false, _triggerSpanTime); //N秒后处理
_saveBatchBlock.TriggerBatch();
Interlocked.Exchange(ref _isProcessing, UnProcessing);
});
}
}
/// <summary>
/// 入队
/// </summary>
/// <param name="msg"></param>
public void Enqueue(T msg)
{
_saveBatchBlock.Post(msg);
TriggerBatch();
}
}
[Serializable]
public class EventArgs<T> : EventArgs
{
public T Argument;
public EventArgs() : this(default)
{
}
public EventArgs(T argument)
{
Argument = argument;
}
}
//=================== 使用方式===============
MemoryQueueBase<LiveMessageHistory> _handleQueue;
/// <summary>
/// 初始化队列
/// </summary>
private void InitQueue()
{
// 合并200条信息在处理,3000毫秒后信息不够200条直接处理
_handleQueue = new MemoryQueueBase<Model>(3000, 200);
// 添加处理方法
_handleQueue.ProcessItemFunction += (entityList) =>
{
//TODO:处理数据
};
// 异常处理
_handleQueue.ProcessException += (obj, e) =>
{
_logger.LogError((Exception)obj, "批量处理失败");
};
}
// 入队
_handleQueue.Enqueue(Model);