异步写入重定向的标准输出是同步的还是异步的?
Are async writes to a redirected stdout synchronous or async?
documentation for Console states:
To redirect the standard input, standard output, or standard error stream, call the Console.SetIn, Console.SetOut, or Console.SetError method, respectively. I/O operations that use these streams are synchronized, which means that multiple threads can read from, or write to, the streams. This means that methods that are ordinarily asynchronous, such as TextReader.ReadLineAsync, execute synchronously if the object represents a console stream.
这是否意味着即使输出重定向到文件,这段代码也会以同步方式执行?
Console.Out.WriteLineAsync("Hello World Async");
现实世界的场景是:如果我的程序将日志输出到标准输出,并且以输出重定向到文件或通过管道传输到另一个程序的方式调用它,如果缓冲区没有被消耗,我的程序会停止吗由 OS 或管道收件人及时处理?
或者,任何人都可以想出一种方法来确定这两种方式吗?
If my program outputs logs to stdout, and it's called in a manner where the output is redirected to a file or piped to another program, will my program stall if the buffer isn't consumed in a timely manner by the OS or pipe recipient?
是的,会的。
当你调用Console.SetOut()
时,这段代码执行:
public static void SetOut(TextWriter newOut) {
if (newOut == null)
throw new ArgumentNullException("newOut");
Contract.EndContractBlock();
#pragma warning disable 618
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
#pragma warning restore 618
#if FEATURE_CODEPAGES_FILE // if no codepages file then we are locked into default codepage and this field is not used
_isOutTextWriterRedirected = true;
#endif
newOut = TextWriter.Synchronized(newOut);
lock(InternalSyncObject) {
_out = newOut;
}
}
即,新输出 TextWriter
总是 由 TextWriter.Syncronized()
编辑的作者 return。这反过来总是 return 是 class 的 SyncTextWriter
object. And if you look at the WriteLineAsync()
方法,它做了两件事:
- 它本身是一个同步方法,这意味着如果多个线程尝试使用编写器,一次只有一个线程会实际这样做(即所有带有该注释的方法在线程之间都是互斥的)。
- 与您的问题更相关的是,
WriteLineAsync()
方法在所使用的底层 TextWriter
上调用 synchronous WriteLine()
方法创建那个 SyncTextWriter
对象。
第二点意味着方法在操作完成之前不会return。它总是 return 是一项已完成的任务。它无法 return 一个未完成的任务,并且如果操作由于任何原因被阻塞,例如输出流的消费者没有使用它并因此导致输出缓冲区被填满,调用不会 return.
这将导致您在其中进行此类调用的线程阻塞,直到写入操作完成为止,即首先导致它阻塞的任何条件都已解决。
现在,综上所述:
- 如果您正在写入文件,无论是因为您调用了
SetOut()
还是因为进程标准输出已被重定向,这可能会减慢程序速度,具体取决于您的程序相对于其他工作生成的输出量确实如此,但我怀疑您是否会遇到任何以“失速”为特征的事情。 IE。磁盘 I/O 很慢,但通常不会长时间阻塞。
- 如果您调用
SetOut()
并传递一些其他类型的写入器,大概您可以控制它并且可以简单地确保它被正确写入并且在写入时不会阻塞。
- 如果 stdout 从外部重定向到某个其他目标,例如第二个 .NET 程序 运行ning 已经启动了您的进程并重定向了
Process.StandardOutput
流,那么它应该是其他程序如果它关心您的程序能够继续畅通无阻,以确保它跟上。但无论如何总是如此。进程输出流的使用者应始终确保尽快阅读它们。
我个人认为,程序本身并不关心保护自己免受可能阻塞的重定向流的影响,当然除了不在内部将控制台流重定向到可能阻塞的目标对象之外。通常,这被认为是接收进程的工作,因为它大概关心重定向的程序是否可以完成它的工作。
如果这真的是一个问题,您可以在进程中自己缓冲输出,例如让 BlockingCollection<string>
对象在想要写入输出的线程和执行实际操作的消费者线程之间进行调解写作。但这并不是那么有用;它只是把罐头踢到路上,因为如果不能指望消耗线程能够畅通无阻地进行,您不想无限期地写入集合。如果消费者真的被阻塞了,集合就会变得太大并在某个时候抛出 OutOfMemoryException
。任何其他试图将写入委托给不同线程的机制都会遇到类似的问题,目的是让当前线程 运行 不阻塞。
恕我直言,最好只是正常编写(即,甚至不要费心使用 ...Async
方法,除非发生这种情况,因为您有代码将写作抽象为 TextWriter
并且它旨在在支持时异步工作),并指望流的消费端“做正确的事”,而不是阻塞你的进程。
documentation for Console states:
To redirect the standard input, standard output, or standard error stream, call the Console.SetIn, Console.SetOut, or Console.SetError method, respectively. I/O operations that use these streams are synchronized, which means that multiple threads can read from, or write to, the streams. This means that methods that are ordinarily asynchronous, such as TextReader.ReadLineAsync, execute synchronously if the object represents a console stream.
这是否意味着即使输出重定向到文件,这段代码也会以同步方式执行?
Console.Out.WriteLineAsync("Hello World Async");
现实世界的场景是:如果我的程序将日志输出到标准输出,并且以输出重定向到文件或通过管道传输到另一个程序的方式调用它,如果缓冲区没有被消耗,我的程序会停止吗由 OS 或管道收件人及时处理?
或者,任何人都可以想出一种方法来确定这两种方式吗?
If my program outputs logs to stdout, and it's called in a manner where the output is redirected to a file or piped to another program, will my program stall if the buffer isn't consumed in a timely manner by the OS or pipe recipient?
是的,会的。
当你调用Console.SetOut()
时,这段代码执行:
public static void SetOut(TextWriter newOut) {
if (newOut == null)
throw new ArgumentNullException("newOut");
Contract.EndContractBlock();
#pragma warning disable 618
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
#pragma warning restore 618
#if FEATURE_CODEPAGES_FILE // if no codepages file then we are locked into default codepage and this field is not used
_isOutTextWriterRedirected = true;
#endif
newOut = TextWriter.Synchronized(newOut);
lock(InternalSyncObject) {
_out = newOut;
}
}
即,新输出 TextWriter
总是 由 TextWriter.Syncronized()
编辑的作者 return。这反过来总是 return 是 class 的 SyncTextWriter
object. And if you look at the WriteLineAsync()
方法,它做了两件事:
- 它本身是一个同步方法,这意味着如果多个线程尝试使用编写器,一次只有一个线程会实际这样做(即所有带有该注释的方法在线程之间都是互斥的)。
- 与您的问题更相关的是,
WriteLineAsync()
方法在所使用的底层TextWriter
上调用 synchronousWriteLine()
方法创建那个SyncTextWriter
对象。
第二点意味着方法在操作完成之前不会return。它总是 return 是一项已完成的任务。它无法 return 一个未完成的任务,并且如果操作由于任何原因被阻塞,例如输出流的消费者没有使用它并因此导致输出缓冲区被填满,调用不会 return.
这将导致您在其中进行此类调用的线程阻塞,直到写入操作完成为止,即首先导致它阻塞的任何条件都已解决。
现在,综上所述:
- 如果您正在写入文件,无论是因为您调用了
SetOut()
还是因为进程标准输出已被重定向,这可能会减慢程序速度,具体取决于您的程序相对于其他工作生成的输出量确实如此,但我怀疑您是否会遇到任何以“失速”为特征的事情。 IE。磁盘 I/O 很慢,但通常不会长时间阻塞。 - 如果您调用
SetOut()
并传递一些其他类型的写入器,大概您可以控制它并且可以简单地确保它被正确写入并且在写入时不会阻塞。 - 如果 stdout 从外部重定向到某个其他目标,例如第二个 .NET 程序 运行ning 已经启动了您的进程并重定向了
Process.StandardOutput
流,那么它应该是其他程序如果它关心您的程序能够继续畅通无阻,以确保它跟上。但无论如何总是如此。进程输出流的使用者应始终确保尽快阅读它们。
我个人认为,程序本身并不关心保护自己免受可能阻塞的重定向流的影响,当然除了不在内部将控制台流重定向到可能阻塞的目标对象之外。通常,这被认为是接收进程的工作,因为它大概关心重定向的程序是否可以完成它的工作。
如果这真的是一个问题,您可以在进程中自己缓冲输出,例如让 BlockingCollection<string>
对象在想要写入输出的线程和执行实际操作的消费者线程之间进行调解写作。但这并不是那么有用;它只是把罐头踢到路上,因为如果不能指望消耗线程能够畅通无阻地进行,您不想无限期地写入集合。如果消费者真的被阻塞了,集合就会变得太大并在某个时候抛出 OutOfMemoryException
。任何其他试图将写入委托给不同线程的机制都会遇到类似的问题,目的是让当前线程 运行 不阻塞。
恕我直言,最好只是正常编写(即,甚至不要费心使用 ...Async
方法,除非发生这种情况,因为您有代码将写作抽象为 TextWriter
并且它旨在在支持时异步工作),并指望流的消费端“做正确的事”,而不是阻塞你的进程。