我正在创建一个应用程序(logviewer),它将显示xml文件中的日志。注意:每个xml文件可以有一个或多个记录。我的应用程序必须在DatagridView控件中将每条记录显示为一行。
基本上它将执行以下任务:
1.来自Do_Work =>解析每个xml文件并将记录添加到列表中 2.如果List大小达到100,则调用ProgressChanged事件以更新包含100条记录的UI(DataGridView)。 3.重复此过程,直到所有xml文件中的所有记录都附加到UI(DataGridView)
实施
DoWork会将每条记录添加到原始列表中,如果列表计数变为100,则会创建一个新的List Collection并调用ProgressChanged事件。
如下:
bWorkerReadXmlLogs.ReportProgress(0, new List<XmlLogRecord>(lstXmlLogRecords)); //memory leak
现在,ProgressChanged事件处理程序会将100条记录追加到DataGridView。
但如果我运行带有数千个xml文件的应用程序,它会占用大量内存并且根本不会释放。 见下图:(申请以3,000K开始)
但如果我只是传递原始集合引用变量,如下所示 - 它可以工作:
bWorkerReadXmlLogs.ReportProgress(0, lstXmlLogRecords); //it works
但问题出在时间ProgressChanged
事件处理程序将所有100条记录追加到DataGridView
Control DoWork
(因为DoWork和ProgressChanged事件之间没有同步)将其清除(原始值) Collection)添加另一组记录并ProgressChanged
抛出集合修改的异常。
现在我需要知道如何通过从原始集合中复制/创建新集合来释放内存:
我的分析:
我可以传递原始集合引用变量并在DoWork
后台线程中等待更多时间,以确保Main / UI Thread将所有100条记录添加到DataGridView,但它不是最佳解决方案,如果用户在某些低端配置中运行应用程序它可能无法确定它是否始终有效。
示例代码:
private void bWorkerReadXmlLogs_DoWork(object sender, DoWorkEventArgs e)
{
try
{
//declare a xmlLogRecords list to hold the list of log records to be displayed on the UI
List<XmlLogRecord> lstXmlLogRecords = new List<XmlLogRecord>();
//loop through the records sent by the GetLogDetails() function and add it to the list
foreach (XmlLogRecord record in GetLogDetails(path))
{
//cancel the background thread if the cancel was requested from the user.
if (bWorkerReadXmlLogs.CancellationPending)
{
e.Cancel = true;
return;
}
//add log record to the list
lstXmlLogRecords.Add(record);
/*if the list contains 100 items then invoke the progresschanged event
where it appends the 100 items into the LogViewer UI (DataGridView)*/
if (lstXmlLogRecords.Count % 100 == 0)
{
//block/wait on background thread so that processor allocates some cycles to work on UI/Main thread to update the records on the DatagridView
Thread.Sleep(200);
bWorkerReadXmlLogs.ReportProgress(0, new List<XmlLogRecord>(lstXmlLogRecords));//Memory Leak here,if i pass original collection reference it works but DoWork clears it immediatly to add new set of records
//clear the List to start/add remaining items.
lstXmlLogRecords.Clear();
}
}
//invoke the ProgressChanged Event for updating the DataGridView if the List contains less than 100 items greater than 0 items
if (lstXmlLogRecords.Count > 0)
{
//Invoke ProgressChanged Event to update the records on the DataGridView
bWorkerReadXmlLogs.ReportProgress(0, lstXmlLogRecords);
}
}
catch (Exception ex)
{
Write_Trace(ex.Message);
}
}
private void bWorkerReadXmlLogs_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
try
{
var rowIndex = 0;
if (e.UserState is List<XmlLogRecord>)
{
//Cast the UserState object into the List<XmlLogRecord>
var records = e.UserState as List<XmlLogRecord>;
//iterate over all the records sent from DoWork event and append the each record into the LogViewer control DataGridView UI
foreach (var record in records)
{
//get the next row Index where the new row has to be placed.
rowIndex = dgvLogViewer.Rows.Count;
//add an emtpy row to add the empty cells into it
dgvLogViewer.Rows.Add();
//set the LogViewer properties if not set already
if (!IsLogviewerPropertiesSet)
{
SetLogViewerProperties();
}
//Disable the Column selection for image columns
DisableImageColumnsSelection(rowIndex);
//Add Data for normal or text cells into the LogViewer Control (DatagridView)
AddLogviewerTextCells(rowIndex, record);
//Add Icons in Image Columns into the LogViewer control (DataGridView)
AddLogviewerImageCells(rowIndex, record);
}
//Sort the LogViewer control (DataGridView) by Datetime column(Index = 2) in Descending order.
dgvLogViewer.Sort(dgvLogViewer.Columns[MKeys.DTTIME], ListSortDirection.Descending);
dgvLogViewer.Columns[MKeys.DTTIME].HeaderCell.SortGlyphDirection = SortOrder.Descending;
if (!IsLogviewerSortingDone)
{
//call selectedindex changed event of the selected record in the datagridview
dgvLogViewer_SelectionChanged(null, null);
IsLogviewerSortingDone = true;
}
}
}
catch (Exception ex)
{
Write_Trace(ex.Message);
}
}
}