声明为类变量的输出缓冲区
private Queue<String> __OutputBuffer = new Queue<String>();
定时器用于每100毫秒处理输出
new System.Timers.Timer()
{
Interval = 100,
Enabled = true
}.Elapsed += new ElapsedEventHandler(
(caller, args) =>
{
ProcessOutput();
}
);
处理队列
private void ProcessOutput()
{
if (__OutputBuffer.Count > 0 && !String.IsNullOrEmpty(__OutputBuffer.Peek()))
{
object _Item = __OutputBuffer.Dequeue();
if(_Item is String)
{
try
{
Browser.DocumentText += "<span style='font-family: Tahoma; font-size: 9pt;'>" + _Item + "</span>";
//Exception On Line Above!
}
catch (Exception) { }
}
}
}
添加到输出缓冲区的方法
private void UpdateOutput(String text)
{
__OutputBuffer.Enqueue(text);
}
我收到了无效的强制转换异常,以下是获取异常时_Item的内容。
**以下引起异常...所以我怀疑它是队列中字符串的内容。
答案 0 :(得分:2)
Queue<>
不是线程安全的,而System.Timers.Timer
在随机池线程上触发其事件。这就是调用ProcessOutput
的位置,您可以在此处调用__OutputBuffer.Dequeue()
并访问Browser.DocumentText
。
您可以使用__OutputBuffer
(lock
和Dequeue
)保护Enqueue
免受并发访问,或使用ConcurrentQueue
。但是,您需要将Browser.DocumentText
分配封送到UI线程,例如使用Control.Invoke
或Control.BeginInvoke
。
答案 1 :(得分:1)
正如Noseratio所说,Queue<>
不是线程安全的,但如果您不希望在项目中使用锁定并且使用的是.NET 4.0或更新版本,则可以使用ConcurrentQueue<>
类,即线程安全。
您需要进行一些更改,例如没有Peek
或Dequeue
方法,您必须使用TryPeek
和TryDequeue
。但它不应该需要太多重大更改,它甚至可以让您进行一些优化,因为如果Queue为空,则两个try方法将返回false
,因此您不再需要Count
检查。
private void ProcessOutput()
{
string output;
if (__OutputBuffer.TryDequeue(out output) && !String.IsNullOrEmpty(output))
{
try
{
Browser.DocumentText += "<span style='font-family: Tahoma; font-size: 9pt;'>" + output + "</span>";
}
catch (Exception) { } // <--- Blindly catching exceptsions is almost never the right thing to do.
}
}
答案 2 :(得分:0)
使用Timer被认为是多线程的。 你有一个新的线程每100毫秒,这可能会导致你出队的竞争条件
使用:
private ConcurrentQueue<String> __OutputBuffer = new ConcurrentQueue<String>();
private void ProcessOutput()
{
string _Item;
if (__OutputBuffer.TryDequeue(out _Item))
{
try
{
Browser.DocumentText += "<span style='font-family: Tahoma; font-size: 9pt;'>" + _Item + "</span>";
//Exception On Line Above!
}
catch (Exception) { }
}
}