保持 'new'ing EventHandler 结果会产生过多的垃圾吗?

3

我正在编写一个C#的Windows应用程序来从串口获取输入,我参考了下面展示的示例代码:

private void serialPort_DataReceived_1 (object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
    if (this.serialPort.IsOpen == true)
        {
            this.BeginInvoke(new EventHandler(delegate { this.textBox1.AppendText(this.serialPort.ReadExisting()); }));                
        }

}

代码工作得很好。但我只是想知道如果BeginInvoke运行多次,那么它是否会在内存中创建许多未使用的“new EventHandler”?

你有充分的理由相信它会在内存中创建“新的EventHandler”,还是你只是过于担心了? - Bryan Crosby
不,我没有。我只是想知道在编程中使用匿名委托是否会是一种不好的实践,因为似乎无法重用这样“创建”的委托。 无论如何,非常感谢所有回复。 :) - hongcc
2个回答

3
一些粗略的计算。串口最高运行速率为115,000波特,即最多每秒11,500字节。最坏情况是每个字节都会触发DataReceived事件,虽然这在技术上是可能的,但由于Control.BeginInvoke()的开销,这将导致非常糟糕的结果。这是你需要修复的问题。
32位版本的CLR需要32个字节的委托对象。因此,您最多会消耗11,500 x 32 = 368 KB /秒的堆内存。这全部分配在第0代堆中,几乎肯定会被第0代垃圾回收完全删除,因为委托对象的生存时间非常短。
默认的第#0代堆大小为2兆字节。当堆受到压力时,它会增长,当第0代集合经常将对象移动到第1代时,它会增加到8兆字节或更多。在这种情况下不太可能发生,因为委托对象的生存时间非常短。让我们使用2兆字节。
因此,仅通过委托对象就需要5.4秒来填满第0代堆并触发垃圾回收。第0代收集平均需要5毫秒,尽管您很少需要那么多时间,因为需要移动的存活对象非常少。
因此,用于处理这些对象的收集所花费的处理时间百分比最高为0.09%,这是不可观察到的。

3

每个匿名委托完成后都可以进行垃圾回收,因此这可能并不是什么大问题。

如果您真的很担心,可以将单个EventHandler创建为类字段:

EventHandler readFromSerialHandler;

然后在你的构造函数中将其设置为

readFromSerialHandler = (s, e) => 
           this.textBox1.AppendText(this.serialPort.ReadExisting());

现在你的DataReceived处理程序将简单地如下所示:

private void serialPort_DataReceived_1 (object sender, SerialDataReceivedEventArgs e) {
    if (this.serialPort.IsOpen == true) {
            this.BeginInvoke(this.readFromSerialHandler);                
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接