【发布时间】:2021-04-15 16:27:14
【问题描述】:
我有以下场景。
我通过逐行读取巨大的 csv 文件来执行拆分功能。
每行都有categoryId。
基于该 ID,我需要将此行写入单独的文件。
为此,我正在执行以下操作:
- 逐行读取大文件。
- 阅读每一行后,我会根据 categoryId 打开一个新流(仅当流尚未打开时)。将行写入流,然后保持流打开,因为大文件中可能会有更多行。
- 最后,处理完大文件中的所有行后,我将关闭所有打开的流。这会强制刷新并关闭连接。
我的问题是。我是否需要手动调用 Flush() 让我们说 -> 每记录 100 行,或者这是由 StreamWriter 本身处理的东西。我在网上读到有一个缓冲区满时会自动刷新,但我不确定这是否属实。我担心的是,如果它不刷新并等待大文件结束,我可能最终会将整个文件加载到内存中。
这是代码的一部分,看看我在说什么:
try
{
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
var locationId = line.Split(',')[0];
var gdProjectId = GetGDProjectId(locationId);
var blobName = $"{gdProjectId}/{DateTime.UtcNow.ToString("dd-MM-yyyy")}/{DateTime.UtcNow.ToString("HH-mm-ss")}-{Guid.NewGuid()}.csv";
if (!openWriters.ContainsKey(gdProjectId))
{
var blockBlobClient = containerClient.GetBlockBlobClient(blobName);
var newWriteStream = await blockBlobClient.OpenWriteAsync(true);
openWriters.Add(gdProjectId, new StreamWriter(newWriteStream, Encoding.UTF8));
}
var writer = openWriters[gdProjectId];
await writer.WriteLineAsync(line);
// SHOULD I MANUALLY INVOKE FLUSH ON EVERY {X} lines processed ?
// TODO: Check if we need to manually flush or the streamwriter does it for us when the buffer is full.
// await writer.FlushAsync();
}
}
catch (Exception ex)
{
throw;
}
finally
{
// we are always closing the writers no matter if the operation is successful or not.
foreach (var oStream in openWriters)
{
oStream.Value.Close();
}
}
【问题讨论】:
-
总是发布代码而不是图片。有时人们想复制一些代码并将其放在他们的答案中。
-
在你写完之前你绝对不想调用 Flush()。这样做会导致缓冲区在充满之前被刷新,从而破坏了缓冲区的用途。在写入所有数据后显式调用 Flush() 是一种很好的做法,但如前所述,退出 using { } 块将隐式执行此操作。我更喜欢显式调用它,因为如果有异常写入底层流,诊断会更容易一些。
-
大家好,我用代码而不是屏幕截图更新了我的示例。 @glenebob 我的问题是否以及何时由 streamWriter 调用自动刷新?我想可以说如果缓冲区是 1024 -> 填满后它会自动刷新并写入目标流,对吗?我担心的是不要在内存中加载太多数据并消耗机器的整个 RAM。你知道在自动刷新之前我可以在作者中容纳多少个字符吗?我将处于一种情况,我可以同时拥有很多打开的流,我不想消耗所有的内存。
-
“我不想消耗所有的内存” - 你无法控制它。即使您调用
Flush(),即使StreamWriter.Flush()方法显式刷新底层流,文件I/O 的层也比这多,例如操作系统缓存。更重要的是,这些缓冲区只有一些 K 大;它们太小了,不会对内存开销产生任何实质性影响,即使有,无论是否刷新缓冲区都存在。明确调用Flush()的唯一原因是当您有一些特定 理由来确保数据已经... -
... 写入,例如您正在写入网络流并且不希望数据延迟,或者您正在写入日志文件并希望确保每一行已经写了以防进程崩溃,诸如此类。另请注意,@jdweng 的上述评论大多是错误的。没有计时器,并且您在关闭编写器时不需要调用
Flush(),因为关闭/处置编写器将始终作为该操作的一部分自动刷新数据。