如果一个线程池中的线程在写文件时应用程序关闭会发生什么?

5
假设您使用一个ThreadPool执行一些操作,并假设每个操作都会写入文件。所有ThreadPool的线程都是后台线程,因此当关闭应用程序时它们将被终止。如果在ThreadPool的线程正在将文件写入磁盘时关闭应用程序,会发生什么?

它会关闭文件句柄并停止向其写入,可能导致文件处于损坏状态。你试过这样做吗? - Yuval Itzchakov
2
我应该通过编写一个大文件来验证发生了什么。 - enzom83
2
结果很可能会因所使用的文件系统而有所不同。FAT将显示与NTFS不同的结果。 - TGlatzer
2
一般来说,如果你发现自己需要回答这些问题(这本身就很有用,别误会了),那可能是你应该改变设计,采用显然正确的方法,而不是依赖框架的恩惠。换句话说,如果你必须确保操作完成,就不要使用后台线程——或者假设你的应用程序随时可以关闭并留下其文件损坏,并在启动时相应地处理此场景。 - Jeroen Mostert
3个回答

7

操作系统会在终止进程时关闭文件句柄。

  • 任何挂起的异步I/O将被取消
  • 未刷新的写缓冲区中的任何数据都将丢失

4

请阅读 前台线程和后台线程 的MSDN文章。

线程池线程是后台线程。根据文章:

当运行时停止后台线程因为进程关闭时,线程不会抛出异常。

线程只是停止了。它执行了一条指令,但不会执行下一条。FileStream将作为CLR清理的一部分而关闭。


CLR不会关闭文件,而是底层操作系统完成这个任务。 - Sriram Sakthivel
如果一个进程正常终止,那么将调用 finalizers:请参见 http://blog.stephencleary.com/2009/08/finalizers-at-process-exit.html。FileStream 的 finalizer 会关闭底层文件句柄。 - canton7
只有在实例不可访问时,终结器才会作为垃圾回收的一部分被调用。当线程池使用FileStream进行写操作时,它是如何变得不可达的? - Sriram Sakthivel
1
一个简单的演示应用程序表明在终止之前CLR确实关闭了FileStream。我在ThreadPool上启动了一个新的工作项,它打开一个文件并等待一秒钟,然后有一个Finalizer试图关闭文件。应用程序在那一秒钟内被终止。在Finalizer中尝试关闭文件会导致ObjectDisposedException,这表明CLR已经处理了FileStream对象。或者是我误解了什么? - canton7
我刚刚也检查了一下。我使用静态字段进行了测试。在Winforms应用程序中,终结器会被调用,但是当使用关闭按钮关闭控制台应用程序时,它不会被调用。您的答案似乎也是正确的。+1 - Sriram Sakthivel

2
您可以通过调用Environment.Exit(0)Environment.Exit(1)方法(成功退出和带错误退出)轻松尝试此情况。我认为所有文件的句柄都将被移除,只有已经写入磁盘的数据将保留,而不会刷新缓冲区。

然而,在进程退出时发生错误的情况下,该文件可能会因为一些奇怪的错误变得无法访问,例如The file is being used by another process之类的错误。


2
你传递给 .Exit() 的参数并不重要;在这两种情况下,应用程序都会正常终止(也就是说,finally 块和 finalizers 将运行)。在你进行实验时,请尝试使用 Environment.FailFast(),因为与 Exit() 不同,它不是一种优雅的终止方式,并且将绕过 finalizers。请注意,刷新缓冲区是可以在 finalizer 中完成的事情,但 BCL 类通常不会这样做(这很遗憾,因为操作系统无论如何都会关闭句柄,这是 finalizer 唯一的附加值)。 - Jeroen Mostert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接