C#中的阻塞队列,在进行生产者、消费者消息通信时,可以大幅提升效率,队列无数据或满时进行阻塞。
/// <summary>
/// Description: 阻塞队列(泛型)
/// 主要实现了队列为空时出队阻塞,队列已满时入队阻塞
/// Author: BruceZhang
/// Date:2017-4-18
/// </summary>
public class BlockingQueue<T>
{
#region Fields & Properties
//队列名称
private string m_name;
//队列最大长度
private readonly int m_maxSize;
//FIFO队列
private Queue<T> m_queue;
//是否运行中
private bool m_isRunning;
//入队手动复位事件
private ManualResetEvent m_enqueueWait;
//出队手动复位事件
private ManualResetEvent m_dequeueWait;
//输出日志
public Action<string> m_actionOutLog;
/// <summary>
/// 队列长度
/// </summary>
public int Count => m_queue.Count;
#endregion
#region Ctor
public BlockingQueueint maxSize, string name = "BlockingQueue", bool isRunning = false)
{
m_maxSize = maxSize;
m_name = name;
m_queue = new Queue<T>m_maxSize);
m_isRunning = isRunning;
m_enqueueWait = new ManualResetEventfalse); // 无信号,入队waitOne阻塞
m_dequeueWait = new ManualResetEventfalse); // 无信号, 出队waitOne阻塞
}
#endregion
#region Private Method
private void OutLogstring message)
{
m_actionOutLog?.Invokemessage);
}
#endregion
#region Public Method
/// <summary>
/// 开启阻塞队列
/// </summary>
public void Open)
{
m_isRunning = true;
}
/// <summary>
/// 关闭阻塞队列
/// </summary>
public void Close)
{
// 停止队列
m_isRunning = false;
// 发送信号,通知出队阻塞waitOne可继续执行,可进行出队操作
m_dequeueWait.Set);
}
/// <summary>
/// 入队
/// </summary>
/// <param name="item"></param>
public void EnqueueT item)
{
if !m_isRunning)
{
// 处理方式一 可直接抛出异常
//throw new InvalidCastException"队列终止,不允许入队");
// 处理方式二 可输出到日志,体验更好
OutLog$"{m_name} 队列终止,不允许入队");
return;
}
while true)
{
lock m_queue)
{
// 如果队列未满,继续入队
if m_queue.Count < m_maxSize)
{
m_queue.Enqueueitem);
// 置为无信号
m_enqueueWait.Reset);
// 发送信号,通知出队阻塞waitOne可继续执行,可进行出队操作
m_dequeueWait.Set);
// 输出日志
OutLog$"{m_name} 入队成功.");
break;
}
}
// 如果队列已满,则阻塞队列,停止入队,等待信号
m_enqueueWait.WaitOne);
}
}
/// <summary>
/// 出队
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public bool Dequeueref T item)
{
while true)
{
if !m_isRunning)
{
lock m_queue) return false;
}
lock m_queue)
{
// 如果队列有数据,则执行出队
if m_queue.Count > 0)
{
item = m_queue.Dequeue);
// 置为无信号
m_dequeueWait.Reset);
// 发送信号,通知入队阻塞waitOne可继续执行,可进行入队操作
m_enqueueWait.Set);
// 输出日志
OutLog$"{m_name} 出队成功.");
return true;
}
}
// 如果队列无数据,则阻塞队列,停止出队,等待信号
m_dequeueWait.WaitOne);
}
}
#endregion
}
