C# 写入文件时复制文件是否线程安全?

C# 写入文件时复制文件是否线程安全?,c#,.net,multithreading,C#,.net,Multithreading,使用类写入文件和同时复制文件的方法是线程安全的吗?看起来操作系统应该安全地处理对文件的并发访问,但我找不到任何关于这方面的文档。我已经编写了一个简单的应用程序进行测试,并且看到了奇怪的结果。文件的副本显示为2MB,但当我用notepad++检查文件内容时,里面是空的。原始文件包含数据 using System; using System.Threading.Tasks; using System.Threading; using System.IO; namespace ConsoleAppl

使用类写入文件和同时复制文件的方法是线程安全的吗?看起来操作系统应该安全地处理对文件的并发访问,但我找不到任何关于这方面的文档。我已经编写了一个简单的应用程序进行测试,并且看到了奇怪的结果。文件的副本显示为2MB,但当我用notepad++检查文件内容时,里面是空的。原始文件包含数据

using System;
using System.Threading.Tasks;
using System.Threading;
using System.IO;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            string filePath = Environment.CurrentDirectory + @"\test.txt";
            using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite))
            {
                Task fileWriteTask = Task.Run(() =>
                    {
                        for (int i = 0; i < 10000000; i++)
                        {
                            fileStream.WriteByte((Byte)i);
                        }
                    });

                Thread.Sleep(50);
                File.Copy(filePath, filePath + ".copy", true);
                fileWriteTask.Wait();
            }
        }
    }
}

谢谢你的帮助

这是线程安全的,因为C对象都不会损坏

操作的结果或多或少会是随机的空文件、部分复制、拒绝访问,这取决于用于为每个操作打开文件的共享模式


如果仔细设置,可以产生合理的结果。即,在每行之后刷新文件并指定兼容的共享模式将允许合理地确保复制完整的行

这是线程安全的,因为C对象都不会损坏

操作的结果或多或少会是随机的空文件、部分复制、拒绝访问,这取决于用于为每个操作打开文件的共享模式


如果仔细设置,可以产生合理的结果。即,在每行之后刷新文件并指定兼容的共享模式将允许合理地确保复制完整的行

答案是否定的。通常情况下,您无法从不同线程对文件系统对象进行操作,也无法为文件内容获得一致或可预测的结果

单个.NETFramework函数可能是安全的,也可能不是安全的,但这并没有多大影响。从磁盘上的各个文件读取、写入或复制数据的时间和顺序基本上是不确定的。我的意思是,如果你多次做同样的事情,你会得到不同的结果,这取决于你无法控制的因素,比如机器负载和磁盘布局

情况更糟,因为负责File.Copy的Windows API在系统进程上运行,并且只与您的程序松散地同步

底线是,如果您想要文件级同步,您别无选择,只能使用文件级原语来实现它。这意味着像打开/关闭、冲洗、锁定之类的事情。找到有效的组合并非易事

一般来说,最好将文件上的所有操作都放在一个线程内,并同步对该线程的访问

作为对注释的回答,如果通过对文件进行内存映射来对其进行操作,则在关闭文件之前,内存中的内容不能保证与磁盘上的内容一致。内存中的内容可以在进程或线程之间同步,但磁盘上的内容不能同步

命名互斥锁在进程之间锁定,但不能保证文件系统对象的一致性

文件系统锁是我提到的可以用来确保文件系统一致性的方法之一,但在许多情况下仍然没有保证。您依赖于操作系统使缓存的磁盘内容无效并刷新到磁盘,但并非所有文件都能保证这一点。例如,可能需要使用FILE_FLAG_NO_BUFFERING、FILE_FLAG_OVERLAPPED和FILE_FLAG_WRITE_THROUGH标志,这可能会严重影响性能


任何认为这是一个简单的“一刀切”解决方案的简单问题的人都从未尝试过让它在实践中发挥作用。

答案是否定的。通常,您无法从不同线程对文件系统对象进行操作,也无法为文件内容获得一致或可预测的结果

单个.NETFramework函数可能是安全的,也可能不是安全的,但这并没有多大影响。从磁盘上的各个文件读取、写入或复制数据的时间和顺序基本上是不确定的。我的意思是,如果你多次做同样的事情,你会得到不同的结果,这取决于你无法控制的因素,比如机器负载和磁盘布局

情况更糟,因为负责File.Copy的Windows API在系统进程上运行,并且只与您的程序松散地同步

底线是,如果您想要文件级同步,您别无选择,只能使用文件级原语来实现它。这意味着像打开/关闭、冲洗、锁定之类的事情。找到有效的组合并非易事

一般来说,最好将文件上的所有操作都放在一个线程内,并同步对该线程的访问

作为对注释的回答,如果通过对文件进行内存映射来对其进行操作,则在关闭文件之前,内存中的内容不能保证与磁盘上的内容一致。内存中的内容可以在进程或线程之间同步,但磁盘上的内容不能同步

一个名叫穆的人 tex在进程之间进行锁定,但不能保证文件系统对象的一致性

文件系统锁是我提到的可以用来确保文件系统一致性的方法之一,但在许多情况下仍然没有保证。您依赖于操作系统使缓存的磁盘内容无效并刷新到磁盘,但并非所有文件都能保证这一点。例如,可能需要使用FILE_FLAG_NO_BUFFERING、FILE_FLAG_OVERLAPPED和FILE_FLAG_WRITE_THROUGH标志,这可能会严重影响性能

任何认为这是一个简单的“一刀切”解决方案的简单问题的人都从未尝试过让它在实践中发挥作用。

这要看情况而定

这取决于你说线程安全是什么意思

首先,看看这个构造函数:

public FileStream(string path, FileMode mode, FileAccess access, FileShare share )
请注意最后一个参数,它说明您允许其他线程和进程对该文件执行什么操作。适用于没有该文件的构造函数的默认值是FileShare.Read,这意味着您允许其他人以只读方式查看该文件。如果你写信给它,这当然是不明智的

你基本上就是这样做的,你打开一个文件来写,同时允许其他人读,读包括复制

另外请注意,如果没有这个:fileWriteTask.Wait;在代码结束时,整个函数不是线程安全的,因为文件流可能在您开始编写之前就已经关闭了

Windows确实使文件访问线程安全,但方式非常简单。例如,如果您使用FileShare.None打开文件,它会使file.Copy崩溃,据我所知,使用.Net执行此操作没有一种优雅的方法。Windows用于同步文件访问的一般方法称为乐观并发,这意味着假设您的操作是可能的,否则将失败

在进程之间共享文件是一个常见的问题,实现这一点的方法之一,主要是在进程间通信中

如果你很勇敢并且愿意使用WinAPI和重叠IO,如果我没记错的话,允许很好的文件锁定

此外,曾经有一个神奇的东西叫做,但它已经进入了微软不推荐的技术领域

这取决于你说线程安全是什么意思

首先,看看这个构造函数:

public FileStream(string path, FileMode mode, FileAccess access, FileShare share )
请注意最后一个参数,它说明您允许其他线程和进程对该文件执行什么操作。适用于没有该文件的构造函数的默认值是FileShare.Read,这意味着您允许其他人以只读方式查看该文件。如果你写信给它,这当然是不明智的

你基本上就是这样做的,你打开一个文件来写,同时允许其他人读,读包括复制

另外请注意,如果没有这个:fileWriteTask.Wait;在代码结束时,整个函数不是线程安全的,因为文件流可能在您开始编写之前就已经关闭了

Windows确实使文件访问线程安全,但方式非常简单。例如,如果您使用FileShare.None打开文件,它会使file.Copy崩溃,据我所知,使用.Net执行此操作没有一种优雅的方法。Windows用于同步文件访问的一般方法称为乐观并发,这意味着假设您的操作是可能的,否则将失败

在进程之间共享文件是一个常见的问题,实现这一点的方法之一,主要是在进程间通信中

如果你很勇敢并且愿意使用WinAPI和重叠IO,如果我没记错的话,允许很好的文件锁定


而且,曾经有一个神奇的东西被称为,但它已经进入了Microsoft弃用技术的领域

我不确定这个问题是否与C相关-这似乎更多的是关于Windows API如何处理共享读/写操作。旁注:用文本编辑器检查二进制文件的内容不是最好的检查,确保以二进制模式打开它。您要解决的最大问题是什么?很明显,您的代码无法正常工作。请注意,除非文件副本位于与原始文件不同的物理硬盘上,同时尝试写入两个文件将大大降低磁盘写入速度,因为它需要在每个文件的扇区之间移动磁盘头,而且由于这个过程几乎肯定是IO绑定的,而不是CPU绑定的,所以CPU并发性对您毫无益处。简言之,您应该看到,通过在此处仅使用一个线程,速度会显著提高。我的建议是关闭此问题并提出一个新问题,详细解释您想做什么。我不确定此问题是否与C相关-这似乎更多地是关于Windows API如何处理共享读/写操作。旁注:ch
使用文本编辑器查看二进制文件的内容不是最好的检查方法,请确保以二进制模式打开它。您试图解决的更大问题是什么?很明显,您的代码无法正常工作。请注意,除非文件副本位于与原始文件不同的物理硬盘上,同时尝试写入两个文件将大大降低磁盘写入速度,因为它需要在每个文件的扇区之间移动磁盘头,而且由于这个过程几乎肯定是IO绑定的,而不是CPU绑定的,所以CPU并发性对您毫无益处。简言之,您应该看到,通过在此处仅使用一个线程,速度会显著提高。我的建议是关闭此问题并提出一个新的问题,详细解释您想要做什么。您将需要某种类型的中介来实现这一点。即使在每次写入后刷新,读取也可以读取一个块,然后在下一次写入之前读取下一个块。然后它会看到文件的结尾,然后退出。@JimMischel-我的建议是获得一些看起来可以使用的东西,例如日志文件,但实际上,如果想要获得完整的文件方式,需要编写更多的代码以允许并行写入和复制。。。也就是说,您可以查看的一个变体的源代码,以了解如何在运行时进行克隆。您需要某种类型的中介来实现这一点。即使在每次写入后刷新,读取也可以读取一个块,然后在下一次写入之前读取下一个块。然后它会看到文件的结尾,然后退出。@JimMischel-我的建议是获得一些看起来可以使用的东西,例如日志文件,但实际上,如果想要获得完整的文件方式,需要编写更多的代码以允许并行写入和复制。。。也就是说,我们可以查看一个变体的源代码,以了解如何在运行时进行克隆。-1当然可以。这通常是通过内存映射文件完成的。你可以在windows上锁定文件,如果你需要更好的同步,你可以使用命名的mutexi。如果你认为这是一个答案,我认为你不理解这个问题。参见编辑。-1当然可以。这通常是通过内存映射文件完成的。你可以在windows上锁定文件,如果你需要更好的同步,你可以使用命名的mutexi。如果你认为这是一个答案,我认为你不理解这个问题。请参见编辑。