I have one log file for each day of the month. These files are plain text with some info in each line like the snippet below:
1?2017-06-01T00:00:00^148^3
2?myVar1^3454.33
2?myVar2^35
2?myVar3^0
1?2017-06-01T00:00:03^148^3
...
To process and show this data, I'm developing a WPF application that reads these txt files, parses the lines and saves this data in a SQLite database. Then, I allow the user to make some basic math operations like AVG of a subset.
As these files are too large (over 300mb and 4 million lines each), I'm struggling with memory usage in the ProcessLine method (as far as I know, the reading part is ok for now). The method never finishes and the application enters in break mode by itself.
My code:
private bool ParseContent(string filePath)
{
if (string.IsNullOrEmpty(FilePath) || !File.Exists(FilePath))
return false;
string logEntryDateTimeTemp = string.Empty;
string [] AllLines = new string[5000000]; //only allocate memory here
AllLines = File.ReadAllLines(filePath);
Parallel.For(0, AllLines.Length, x =>
{
ProcessLine(AllLines[x], ref logEntryDateTimeTemp);
});
return true;
}
void ProcessLine(string line, ref string logEntryDateTimeTemp)
{
if (string.IsNullOrEmpty(line))
return;
var logFields = line.Split(_delimiterChars);
switch (logFields[0])
{
case "1":
logEntryDateTimeTemp = logFields[1];
break;
case "2":
LogEntries.Add(new LogEntry
{
Id = ItemsCount + 1,
CurrentDateTime = logEntryDateTimeTemp,
TagAddress = logFields[1],
TagValue = Convert.ToDecimal(logFields[2])
});
ItemsCount++;
break;
default:
break;
}
}
Is there a better way of doing it?
OBS: I've also tested two other methods for reading the file, which are:
#region StreamReader
//using (StreamReader sr = File.OpenText(filePath))
//{
// string line = String.Empty;
// while ((line = sr.ReadLine()) != null)
// {
// if (string.IsNullOrEmpty(line))
// break;
// var logFields = line.Split(_delimiterChars);
// switch (logFields[0])
// {
// case "1":
// logEntryDateTimeTemp = logFields[1];
// break;
// case "2":
// LogEntries.Add(new LogEntry
// {
// Id = ItemsCount + 1,
// CurrentDateTime = logEntryDateTimeTemp,
// TagAddress = logFields[1],
// TagValue = Convert.ToDecimal(logFields[2])
// });
// ItemsCount++;
// break;
// default:
// break;
// }
// }
//}
#endregion
#region ReadLines
//var lines = File.ReadLines(filePath, Encoding.UTF8);
//foreach (var line in lines)
//{
// if (string.IsNullOrEmpty(line))
// break;
// var logFields = line.Split(_delimiterChars);
// switch (logFields[0])
// {
// case "1":
// logEntryDateTimeTemp = logFields[1];
// break;
// case "2":
// LogEntries.Add(new LogEntry
// {
// Id = ItemsCount + 1,
// CurrentDateTime = logEntryDateTimeTemp,
// TagAddress = logFields[1],
// TagValue = Convert.ToDecimal(logFields[2])
// });
// ItemsCount++;
// break;
// default:
// break;
// }
//}
#endregion
OBS2: I'm using Visual Studio 2017, and when the application is running in debug mode, the application suddenly enters in break mode, and the message in the Output window reads as follows:
The CLR has been unable to transition from COM context 0xb545a8 to COM context 0xb544f0 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.