多个生产者一个消费者的单线程处理队列

之前其他博主那里看到的例子,很值得参考

/// <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);
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值