在写入线程安全的同时复制文件?

时间:2014-06-24 03:07:10

标签: c# .net multithreading

使用FileStream类写入文件和.NET File.Copy方法同时复制文件线程安全吗?看起来操作系统应该安全地处理对文件的并发访问,但我找不到任何关于此的文档。我写了一个简单的应用程序来测试,我看到了奇怪的结果。该文件的副本显示为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();
            }
        }
    }
}

感谢您的帮助!

3 个答案:

答案 0 :(得分:2)

在“C#对象都不会被破坏”的意义上它是线程安全的。

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

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

答案 1 :(得分:2)

取决于。

当你说&#34;线程安全&#34;。

时,取决于你的意思

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

public FileStream(string path, FileMode mode, FileAccess access, FileShare share )

注意最后一个参数,它说明了你允许其他线程和进程对文件做什么。适用于没有它的构造函数的默认值是FileShare.Read,这意味着您允许其他人以只读方式查看文件。如果你写信的话,这当然是不明智的。

你基本上做了什么,你打开了一个文件写作,同时允许其他人阅读,并且&#34;阅读&#34;包括复制。

另请注意,如果没有这个:fileWriteTask.Wait();在代码末尾,您的整个函数都不是线程安全的,因为FileStream可能在您开始编写之前就已关闭。

Windows确实使文件访问线程安全,但是以非常简单的方式。例如,如果你打算用FileShare.None打开文件,它就会崩溃File.Copy并且据我所知,使用.Net并不是一个优雅的方法。 Windows用于同步文件访问的一般方法称为乐观并发,意味着假设您的操作是可行的,如果不是,则会失败。

this question discusses waiting for file lock in .Net

在流程之间共享文件是一个常见问题,其中一种方法是执行此操作,主要用于进程间通信memory mapped files,这是the MSDN documentation

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

此外,曾经有一个名为Transactional NTFS的神奇事物,但它已经进入微软贬值技术领域

答案 2 :(得分:0)

答案是否定的。通常,您无法对来自不同线程的文件系统对象进行操作,并且可以为文件内容实现一致或可预测的结果。

单个.NET Framework功能可能是也可能不是安全的,但这并不重要。从磁盘上的各个文件读取,写入或复制数据的时间和顺序基本上是不确定的。我的意思是,如果你多次做同样的事情,你会得到不同的结果,这取决于控制之外的因素,如机器负载和磁盘布局。

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

底线是,如果你想要文件级同步,你别无选择,只能使用文件级原语来实现它。这意味着开/关,冲洗,锁定等。找到有效的组合是非常重要的。

通常,最好将所有操作保留在一个线程内的文件中,并同步对该线程的访问。


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

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

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

任何认为这是一个简单的一刀切解决方案的简单问题的人根本就没想过让它在实践中发挥作用。