C# 应用 - 生产与消费

1. 功能

模拟一个生产、消费的过程,不过这里没有做消息自动通知,而是消费者自己定时过去取。
一般的生产环境应该是在有新消息产生时,将新消息发送给消费者。
拿下面例子来举例,可在数据入队时,调用一个 event 方法,event 触发消费者的方法。
如果是不同设备之间的订阅,要么自己写 http 接受/请求 去实现订阅、通知,要么用成熟的 MQ。

2. 代码实现

public partial class QueueWindow : Window
{
    Queue<int> _queueNum;

    public int Num
    {
        get { return (int)GetValue(NumProperty); }
        set { SetValue(NumProperty, value); }
    }

    public static readonly DependencyProperty NumProperty =
        DependencyProperty.Register("Num", typeof(int), typeof(QueueWindow), new PropertyMetadata(1));

    CancellationTokenSource _source = new CancellationTokenSource();

    int _beginNum = 1;
    int _takeTimeForGetData = 3; // 耗时
    int _getLength = 25; // 从数据库取出的数据的长度
    int _sleepTime = 10; // 定时从数据库取数据的间隔
    int _intervalSpeed = 1; // 从队列取出数据的频率

    public QueueWindow()
    {
        InitializeComponent();            

        this.tb.SetBinding(TextBlock.TextProperty, new Binding("Num") { Source = this });

        Test();
    }

    public void Test()
    {
        RefreshCache();

        GetDataTask();

        ShowDataTask();
    }

    public void RefreshCache()
    {
        if (_queueNum == null)
        {
            _queueNum = new Queue<int>();
        }
        else
        {
            _queueNum.Clear();
        }
    }               

    /// <summary>
    /// 定时从数据库获取数据并入队
    /// </summary>
    void GetDataTask()
    {
        Task.Factory.StartNew(() =>
        {
            while (!_source.IsCancellationRequested)
            {
                // 取数据的耗时
                Thread.Sleep(_takeTimeForGetData * 1000);

                // 模拟从数据库取出数据,并将数据放入队列
                foreach (int i in Enumerable.Range(_beginNum, _getLength))
                {
                    EnQueue(i);
                }
                _beginNum = _beginNum + _getLength;

                // 定时
                Thread.Sleep(_sleepTime * 1000);
            }
        });
    }

    /// <summary>
    /// 定时从队列中取出一个数据并展示
    /// </summary>
    void ShowDataTask()
    {
        Task.Factory.StartNew(() =>
        {
            while (!_source.IsCancellationRequested)
            {

                Tuple<bool, int> res = DeQueue();
                if (res.Item1)
                {
                    App.Current.Dispatcher.Invoke(new Action(() =>
                    {
                        Num = res.Item2;
                    }));
                }

                // 根据每次读取的时间间隔、读取耗时、返回结果的长度来决定 出队 的时间间隔
                double mill = (double)(_takeTimeForGetData + _sleepTime) / (double)_getLength;
                Thread.Sleep((int)(mill * 1000));
            }
        }, TaskCreationOptions.LongRunning);
    }

    object _cacheLockObj = new object();

    /// <summary>
    /// 入队
    /// </summary>
    /// <param name="num"></param>
    public void EnQueue(int num)
    {
        lock (_cacheLockObj)
        {
            if (_queueNum == null) return;

            _queueNum.Enqueue(num);

            // 当需要控制队列的长度时,通过一定的策略删除掉部分数据
        }            
    }

    /// <summary>
    /// 出队
    /// </summary>
    /// <returns></returns>
    public Tuple<bool, int> DeQueue()
    {
        bool hasNum = false;
        int num = 0;

        lock (_cacheLockObj)
        {
            if (_queueNum != null && _queueNum.Count > 0)
            {
                num = _queueNum.Dequeue();
                hasNum = true;
                Console.WriteLine(string.Join(", ", _queueNum.ToList()));
            }                
        }

        return new Tuple<bool, int>(hasNum, num);
    }

    private void MenuItem_Click(object sender, RoutedEventArgs e)
    {
        this.Close();
    }
}
posted @ 2021-03-11 12:31  鑫茂  阅读(93)  评论(0编辑  收藏  举报