题
我有一个情况,在代码中有一个巨大的功能,分析记录的逐行、验证和编写另一个文件。
在情况有错误,在文件,它呼吁另一种功能,拒绝记录并写到拒绝的原因。
由于存在泄漏的程序,它崩溃"SIGSEGV".一个解决方案来"重新启动"文件,从那里崩溃,是写的最后处理的记录以一个简单的文件。
为了实现这一目标,当前记录的数目在处理循环,需要将写入文件。我如何确保数据的复盖在内的文件的循环?
不用fseek先位置/倒退的一个循环内会降低性能吗?
记录的数量可以很大,有时(高达500K).
谢谢。
编辑:存储器泄漏已经得到修复。重新启动解决方案是建议作为 一个额外的安全措施和装置提供一个重新启动机制与一个跳n记录的解决方案。对不起对不提及它早。
解决方案
当面临这样的问题,可以采取两种方法之一:
- 该方法你的建议:每条记录的,你看, 写出来的记录数量 (或位置返回的
ftell
上输入文件),以一个单独的 书签 文件。要确保恢复正是你离开的,因为要不是介绍的重复记录,您必须fflush
之后的每一个编写(两个bookmark
和输出/拒收的文件。) 这一点,并无缓冲写操作在一般情况下,减慢的典型(没有失败)的情况显着。为了完整起见,注意你有三个方法,写入你的书签文件:fopen(..., 'w') / fwrite / fclose
-非常缓慢rewind / truncate / fwrite / fflush
-稍快rewind / fwrite / fflush
- 有点快;你可以跳过truncate
由于记录的人数(或ftell
位置的)将永远是作为长期或更长时间比以前的记录数量(或ftell
位),并将完全复盖,提供你截断该文件一旦启动 (这样的回答你最初的问题)
- 假设一切都会好的 在大多数情况下;当继续失败之后,只需 计记录的数量已经输出 (正常产出加上拒绝),以及跳过相当数量的记录输入文件。
- 这使典型的(没有失败)的情况非常快,而不会明显影响性能的情况下恢复后失败的情况。
- 你不需要
fflush
文件、或至少不经常。你仍然需要fflush
主要的输出文件之前交换以书面形式将拒绝文件,fflush
该文件之前拒绝切换回来写作的主要输出的文件(可能是一个几百或几千倍对于500k-记录输入。) 只需删除最后一个未终止的线从产出/拒收的文件,一切都以这条线将是一致的。
我强烈推荐的方法#2.书写要求的方法#1(取的三种可能性)是极其昂贵的,比任何其他(缓冲)读取所需的方法#2(fflush
可能需要几毫秒;乘以500k和你分钟-而计算线路的数量在500k-文件记录仅仅需要几秒钟而且,更重要的是,缓存文件系统是工作 有,不反对 你在那。)
编辑 只是想澄清的具体步骤,需要实施方法2:
当写入到输出和拒绝文件分别你只需要冲洗当从编写一个文件写到另一个。考虑以下情况作为例证的ncessity做这些冲文件-开关:
- 假设你写1000记录的主要输出的文件,然后
- 你必须写1条,对于拒绝的文件,而不手动冲水的主要输出的文件,然后
- 你写200多行的主要输出的文件,没有手工冲拒绝文件第一,然后
- 运行时 自动 刷新的主要输出的文件给你,因为你已经积累了大量数据在缓冲区的主要输出的文件,即1200记录
- 但 运行时尚未自动刷新反对文件的磁盘给你,因为该文件的缓冲,只包含一个记录,这是不足够数量的自动冲水
- 你的程序的崩溃,在这一点
- 你恢复最1200记录中的主要输出的文件(运行冲洗这些了你),但是0(!) 记录在拒绝文件(不刷新).
- 你恢复处理的输入文件记录#1201,假设你只有1200记录成功处理的主要输出文件;被拒绝的记录将会丧失,并1200个有效的记录将重复
- 你不想要这个!
- 现在考虑的手工冲洗后的开关输出/拒绝文件:
- 假设你写1000记录的主要输出的文件,然后
- 你遇到一个无效的记录属于拒绝接受文件;最后的记录是有效的;这意味着你不切换到书面的拒绝文件:齐平的主要输出文件之前编写的文件拒绝
- 你现在写的1条,对于拒绝的文件,然后
- 你遇到一个有效的记录属于主要输出文件;最后的记录是无效的;这意味着你交换以书面形式向主要的输出文件:刷新反对的文件之前编写的主要输出的文件
- 你写200多行的主要输出的文件,没有手工冲拒绝文件第一,然后
- 假设运行时并没有自动冲水的任何东西给你,因为200记录,缓冲,因为最后手冲上的主要输出的文件都不足以引发一个自动冲水
- 你的程序的崩溃,在这一点
- 你恢复最1000有效记录中的主要输出的文件(你手工冲洗那些在切换到拒绝的文件)和1记录在拒绝文件(你手工冲换前后的主要输出的文件)。
- 你正确地恢复处理的输入文件记录#1001,这是第一个有效的记录后,立即无效记录。
- 你重新处理的下一个200有效的记录,因为他们不刷新,但你没有丢失的记录并没有重复或者
如果你不快乐的时间间隔之间的运行的自动刷新,你还可以做手册,刷新,每100或每1000个的记录。这取决于是否处理记录是更昂贵的冲洗或没有(如果处理是更昂贵,经常冲洗,也许以后的每一个记录,否则只有齐之间切换时,输出/拒绝。)
恢复从失败
- 打开输出的文件和拒绝文件 对于阅读和写作, ,并开始通过阅读和计算的每一个记录(说
records_resume_counter
)直到你达到结束文件 - 除非你是冲洗后 每 记录你输出, 你也将需要执行的一点特殊待遇的最后一条记录都输出和拒绝文件:
- 在阅读之前记录中断的输出/拒绝的文件,请记住你的位置在中所述产出/拒绝文件(使用
ftell
),让我们叫它last_valid_record_ends_here
- 读一下记录。验证的记录不一部分记录(即运行时具有不刷新该文件的 中间 一个记录)。
- 如果你有一个记录每一行,这是很容易验证通过检查最后一个字记录是一回车或进行(
\n
或`r`)- 如果该记录是完成、递增的记录,反而继续进行,与下一个记录(或文件结束,以先到者为准。)
- 如果该记录是局部的,
fseek
回到last_valid_record_ends_here
, 和停止阅读,从这个输出/拒绝文件;不增反;继续下一个输出或拒绝的文件,除非你已经通过他们所有人
- 在阅读之前记录中断的输出/拒绝的文件,请记住你的位置在中所述产出/拒绝文件(使用
- 打开输入文件的阅读和跳过
records_resume_counter
记录从它- 继续处理以及输出的输出/拒绝接受文件;这将自动追加的产出/拒绝文件在你离开的读数已经处理的记录
- 如果你执行特殊处理的部分记录的刷新,下一次记录输出,将复盖其部分信息来自前一个运行(在
last_valid_record_ends_here
)-你不会有任何重复,垃圾或失踪记录。
- 打开输出的文件和拒绝文件 对于阅读和写作, ,并开始通过阅读和计算的每一个记录(说
其他提示
如果您可以更改代码以将最后处理的记录写入文件,为什么不能更改它以修复内存泄漏?
在我看来,这是一个更好的解决方案,可以解决问题的根本原因,而不是治疗症状。
fseek()
和 fwrite()
会降低性能,但远不及开放/写入/关闭类型操作。
我假设您将 ftell()
值存储在第二个文件中(这样您就可以从中断的地方继续)。您应始终 fflush()
该文件,以确保将数据从C运行时库写入OS缓冲区。否则,您的SEGV将确保该值不是最新的。
不是写出整个记录,而是在每个记录的开头调用ftell()并写入文件指针的位置可能更容易。当你必须重新启动程序时,fseek()到文件中的最后一个写入位置并继续。
当然,修复内存泄漏最好;)
如果为每条记录写入最后处理的位置,这将对性能产生显着影响,因为您需要提交写入(通常通过关闭文件),然后再次重新打开该文件。在其他作品中,fseek是你最不担心的。
我会停止挖掘更深的洞,然后通过 Valgrind 运行程序。这样做可以避免泄漏,以及其他问题。