1

我有以下用于配置文件写回的 C# 算法:

string strPathConfigFile = "C:\File.txt"
string strPathBackupFile = "C:\File.backup"
string strContent = "File Content Text";
bool oldFilePresent = File.Exists(strPathConfigFile);

// Step 1
if (oldFilePresent)
{
    File.Move(strPathConfigFile, strPathBackupFile);
}

// Step 2
using (FileStream f = new FileStream(strPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None))
{
    using (StreamWriter s = new StreamWriter(f))
    {
        s.Write(strContent);
        s.Close();
    }
    f.Close();
}

// Step 3
if (oldFilePresent)
{
    DeleteFile(strPathBackupFile);
}

它是这样工作的:

  1. 原来的 File.txt 被重命名为 File.backup。
  2. 新的 File.txt 被写入。
  3. File.backup 被删除。

这样,如果在写入操作期间发生断电,仍然存在完整的备份文件。备份文件只有在写操作完成后才会被删除。读取过程可以检查备份文件是否存在。如果是,则认为普通文件已损坏。

要使这种方法发挥作用,严格遵循 3 个步骤的顺序至关重要。

现在我的问题是:C# 编译器是否可以交换第 2 步和第 3 步?

这可能会带来轻微的性能优势,因为第 1 步和第 3 步包含在相同的 if 条件中,这可能会诱使编译器将它们放在一起。我怀疑编译器可能会这样做,因为第 2 步和第 3 步对完全不同的文件进行操作。对于一个不知道我异常聪明的写回过程语义的编译器,第 2 步和第 3 步可能看起来不相关。

4

2 回答 2

2

根据语言规范,C# 编译器在重新排序语句时必须保留副作用。写入文件就是这样的副作用。

于 2022-01-10T12:20:23.160 回答
0

通常,编译器/抖动/CPU 可以自由地重新排序指令,只要结果对于单个线程是相同的。但是,IO、系统调用和与多线程相关的大多数事情都会涉及内存屏障或其他同步,从而阻止这种重新排序。

在给定的示例中,仅涉及一个线程。因此,如果正确实现了文件 API(这是一个相当安全的假设),就不会有意外行为的风险。

在编写多线程代码时,通常会弹出重新排序问题,而没有意识到同步的所有潜在危险和要求。只要您只使用单个线程,您就不需要关心重新排序的可能性。

于 2022-01-10T12:21:02.653 回答