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();
}
}