我的Windows窗体应用程序读取一个大的.txt
- 文件(1.6 GB)并分析文本,例如: G。它会比较if
- 条件中的某些值。
当读取了约40%的文档时,应用程序不再响应,并且不再更新其progess栏。它不会抛出OutOfMemory-Exeption或其他任何东西,当分析完成时,应用程序会响应用户。
有没有办法避免这种令人不快的冻结,例如: G。通过为我的应用程序分配更多内存?
代码示例(line
和其他变量已声明为global
):
l = File.ReadLines(@path).Count();
ProgressBar.Maximum = l;
int counter = 0;
while ((line = reader.ReadLine()) != null)
{
ProgressBar.Value += 1;
if (line.Contains(date) && (DateTime.Compare(DateTime.Parse(start), DateTime.Parse(line.Substring(19, 8))))
{
if (line.Contains("abcdef"))
{
counter++;
}
}
}
答案 0 :(得分:0)
理想情况下,您应该在其他一些线程上异步读取这些行。这样,您就不会阻止UI线程,冻结您的应用程序。
我对内置async
I / O API的一个问题是粒度:您可以轻松地异步读取单个行或整个文件。如果你想处理线而不是块,那么这些是你唯一的选择。这是不幸的,因为将每一行作为单独的async
操作读取对于大文件来说花费的时间太长,并且一次读取整个文件可能会非常耗费内存。
能够分批读取行(以合理的速度),同时让您有机会处理到目前为止已读取的内容(合理的内存使用情况)。这非常难以实现:
public static class AsyncFile
{
// Tweak as you see fit.
private const int MaxLinesPerBatch = 1000;
private const FileMode ReadMode = FileMode.Open;
private const FileAccess ReadAccess = FileAccess.Read;
private const FileShare ReadShare = FileShare.Read;
public static async Task ReadAllLinesAsync(
string path,
Action<string> lineCallback,
Action<double?> progress = null,
Encoding encoding = null)
{
using (var stream = new FileStream(path, ReadMode, ReadAccess, ReadShare))
using (var reader = new StreamReader(stream, encoding ?? Encoding.UTF8))
{
var fileSize = stream.CanSeek ? stream.Length : default(long?);
var currentProgress = fileSize < 1L ? 0d : 0d / fileSize;
progress?.Invoke(currentProgress);
var lines = new string[MaxLinesPerBatch];
int linesRead;
var batchFunction = CreateBatchReadFunction(reader, lines);
while ((linesRead = await Task.Run(batchFunction)) > 0)
{
for (var i = 0; i < linesRead; i++)
lineCallback(lines[i]);
if (currentProgress == null)
continue;
var newProgress = ((100L * stream.Position) / fileSize) / 100d;
if (newProgress > currentProgress)
progress?.Invoke(currentProgress = newProgress);
}
if (currentProgress < 1d)
progress?.Invoke(1d);
}
}
private static Func<int> CreateBatchReadFunction(
StreamReader reader,
string[] lines)
{
return () =>
{
string line;
var count = 0;
while (count < lines.Length && (line = reader.ReadLine()) != null)
lines[count++] = line;
return count;
};
}
}
读取一批行时,每行调用一次lineCallback
。如果提供,progress
会将当前进度报告为double
和0.0
之间的1.0
,您可以将其乘以进度条的Maximum
。如果无法提前以低成本确定进度,则会报告null
,您可以通过将进度条的IsIndeterminate
设置为true
来表示。
除非另行配置,否则将在调用者的上下文中调用lineCallback
和progress
,因此从任何一个更新UI都是安全的。但是,我会避免在lineCallback
内做大量工作。
用法很简单:
await AsyncFile.ReadAllLinesAsync(
PATH_TO_FILE,
line => { /* do something with line*/ },
progress =>
{
if (progress >= 0d)
progressBar.Value = progress.Value * progressBar.Maximum;
else
progressBar.IsIndeterminate = true;
});