TL; DR我有一个应用程序正在后台从USB设备读取消息,并在屏幕上显示消息。我正在使用BlockingCollection,因为我需要快速读取消息,因此设备无法获得BufferOverflow。
我正在阅读这样的消息(我的制片人):
private void ReadMessages(BlockingCollection<object> logMessages)
{
uint numMsgs;
Status status;
Message[] msgs = new Message[10];
while(!logMessages.IsAddingCompleted)
{
numMsgs = (uint) msgs.Length;
status = readMessages(channel, msgs, ref numMsgs, 1000);
if(status == Status.ERR_BUFFER_OVERFLOW)
{
logMessages.Add("BUFFER OVERFLOW - MESSAGES LOST!");
logMessages.Add(CopyMessages(msgs, numMsgs));
}
else if(status == Status.STATUS_NOERROR)
{
logMessages.Add(CopyMessages(msgs, numMsgs));
}
else
{
throw new Exception("Error");
}
}
readMessages()方法将填充msgs
数组,并读取Message
个对象,numMsgs
引用包含已读取的消息数(最多10个)。我使用了一个名为CopyMessages()
的函数,所以我只传递一个大小合适的Message[]
。即如果读取了5条消息,我发送Message[5]
而不是Message[10]
。
我读了这样的消息(我的消费者):
private void DisplayMessages(BlockingCollection<object> messages)
{
string[] msgs;
try
{
foreach (var item in messages.GetConsumingEnumerable(_cancellationTokenSource.Token))
{
if (item is string)
{
msgs = new string[] { item.ToString() };
}
else if (item is PassThruMsg[])
{
msgs = FormatMessages((PassThruMsg[])item);
}
else
{
msgs = new string[0];
}
Task.Factory.StartNew(new Action(() => outputTextBox.AppendText(String.Join(Environment.NewLine, msgs) + Environment.NewLine)), _cancellationTokenSource.Token, TaskCreationOptions.None, uiContext);
}
}
catch (OperationCanceledException)
{
//TODO:
}
}
我在点击按钮内启动任务,如下所示:
var results = new BlockingCollection<object>();
var display = Task.Factory.StartNew(() => DisplayMessages(results));
var readMessages = Task.Factory.StartNew(() => ReadMessages(results));
Task[] tasks = new Task[] { display, readMessages };
try
{
await Task.Factory.ContinueWhenAll(tasks, result => { results.CompleteAdding(); }, _cancellationTokenSource.Token, TaskContinuationOptions.None, uiContext);
}
catch (TaskCanceledException)
{
//TODO:
}
这样可以正常工作,当无条件运行时,它会毫无问题地从设备打印消息。但是,在设备开始在非常繁重的负载下工作(消费者被如此快速地调用它暂时锁定UI)之后我注意到输出文本框正在重复值。据我所知,GetConsumingEnumerable()
也会从阻止集合中删除项目,但我不知道为什么我会看到多次打印的消息。每条消息都有一个时间戳,当我从设备读取消息时,它会清除缓冲区,因此我知道我没有多次读取该消息。
我在这里遗漏了什么吗?有没有更好的方法来处理这种生产者/消费者场景以确保准确的数据?我已经看过是否有某些地方的引用可能重叠,但我没有看到它。