.net Console.WriteLine是否阻塞?
.net Console.WriteLine是否阻塞?,.net,multithreading,console,.net,Multithreading,Console,Console.WriteLine是否会一直阻塞直到输出被写入,还是会立即返回 如果它阻止,是否有将异步输出写入控制台的方法?是,它阻止。据我所知,框架中没有内置异步控制台写入 在我的办公桌上快速测试一下,会发现是的,它会阻塞 要异步写入控制台,您只需将写入内容发送到另一个线程,然后由该线程将其写入即可。您可以通过让另一个线程旋转来写入消息队列,或者通过链接Task实例来实现这一点,以便对写入进行排序 我之前提出的使用线程池的建议是不好的,因为它不能保证排序,因此,控制台输出可能会混淆。是的,c
Console.WriteLine
是否会一直阻塞直到输出被写入,还是会立即返回
如果它阻止,是否有将异步输出写入控制台的方法?是,它阻止。据我所知,框架中没有内置异步控制台写入 在我的办公桌上快速测试一下,会发现是的,它会阻塞 要异步写入控制台,您只需将写入内容发送到另一个线程,然后由该线程将其写入即可。您可以通过让另一个线程旋转来写入消息队列,或者通过链接
Task
实例来实现这一点,以便对写入进行排序
我之前提出的使用线程池的建议是不好的,因为它不能保证排序,因此,控制台输出可能会混淆。是的,console.WriteLine将阻塞,直到写入输出,因为它调用底层流实例的写入方法。通过调用Console.OpenStandardOutput获取流,然后在该流上调用BeginWrite和EndWrite,可以异步写入标准输出流(称为底层流);请注意,您必须自己进行字符串编码(将System.string对象转换为byte[]),因为流只接受字节数组 编辑-一些示例代码让您开始:
using System;
using System.IO;
using System.Text;
class Program
{
static void Main(string[] args)
{
string text = "Hello World!";
Stream stdOut = Console.OpenStandardOutput();
byte[] textAsBytes = Encoding.UTF8.GetBytes(text);
IAsyncResult result = stdOut.BeginWrite(textAsBytes, 0, textAsBytes.Length, callbackResult => stdOut.EndWrite(callbackResult), null);
Console.ReadLine();
}
}
编辑2-根据Brian Gideon在评论中的建议,这是一个备选版本(请注意,这仅涵盖可用的16个Write&WriteLine重载之一)
实现Begin/End方法作为TextWriter类的扩展,然后添加AsyncConsole类来调用它们:
using System;
using System.IO;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
string text = "Hello World!";
AsyncConsole.WriteLine(text);
Console.ReadLine();
}
}
public static class TextWriterExtensions
{
public static IAsyncResult BeginWrite(this TextWriter writer, string value, AsyncCallback callback, object state)
{
return Task.Factory.StartNew(x => writer.Write(value), state).ContinueWith(new Action<Task>(callback));
}
public static void EndWrite(this TextWriter writer, IAsyncResult result)
{
var task = result as Task;
task.Wait();
}
public static IAsyncResult BeginWriteLine(this TextWriter writer, string value, AsyncCallback callback, object state)
{
return Task.Factory.StartNew(x => writer.WriteLine(value), state).ContinueWith(new Action<Task>(callback));
}
public static void EndWriteLine(this TextWriter writer, IAsyncResult result)
{
var task = result as Task;
task.Wait();
}
}
public static class AsyncConsole
{
public static IAsyncResult Write(string value)
{
return Console.Out.BeginWrite(value, callbackResult => Console.Out.EndWrite(callbackResult), null);
}
public static IAsyncResult WriteLine(string value)
{
return Console.Out.BeginWriteLine(value, callbackResult => Console.Out.EndWriteLine(callbackResult), null);
}
}
请注意,我已经切换到使用任务来管理Begin/End方法:这是因为如果已经有异步写入,BeginWrite方法本身似乎会阻塞
一旦你有了这个类,只需调用注入方法,你对Console.WriteLine的每次调用,不管你在哪里调用,或者使用哪个重载,都会变成异步的。通信ca:
class Program
{
static void Main(string[] args)
{
string text = "Hello World!";
AsyncStreamWriter.InjectAsConsoleOut();
Console.WriteLine(text);
Console.ReadLine();
}
}
是,它将阻塞,直到输出写入屏幕。我不确定文档中是否明确说明了这一点,但您可以通过深入查看reflector中的
控制台类来验证这一点。特别是initializestDoutror()
方法。为输出流创建TextWriter
时,它将AutoFlush
设置为true
Hmya,控制台输出速度不是特别快。但这是一个永远不需要解决的“问题”。关注奖品:你是为了人类的利益而向控制台写信。而那个人不可能读得那么快
如果输出被重定向,它将停止变慢。如果这仍然对你的程序有影响,那么你可能只是写了太多的信息。改为写入文件
Console.WriteLine是否会阻塞,直到
输出已写入或正在写入
马上回来
对
如果它真的阻塞了,有没有一种方法
将异步输出写入
控制台
如果您使用的是.NET4.0,那么在不阻塞的情况下写入控制台的解决方案非常简单。其思想是将文本值排队,并让单个专用线程执行控制台.WriteLine
调用。生产者-消费者模式在这里非常理想,因为它保留了使用本机控制台类时隐含的时间顺序。NET4.0之所以让这变得容易,是因为它有一个类,可以方便地生成生产者-消费者模式。如果您没有使用.NET4.0,则可以通过下载框架获得一个后端口
public static class NonBlockingConsole
{
private static BlockingCollection<string> m_Queue = new BlockingCollection<string>();
static NonBlockingConsole()
{
var thread = new Thread(
() =>
{
while (true) Console.WriteLine(m_Queue.Take());
});
thread.IsBackground = true;
thread.Start();
}
public static void WriteLine(string value)
{
m_Queue.Add(value);
}
}
公共静态类非阻塞控制台
{
私有静态BlockingCollection m_队列=新建BlockingCollection();
静态非阻塞控制台()
{
var线程=新线程(
() =>
{
while(true)Console.WriteLine(m_Queue.Take());
});
thread.IsBackground=true;
thread.Start();
}
公共静态void WriteLine(字符串值)
{
m_队列。添加(值);
}
}
Console.WriteLine
同步运行,Console.Out
流将在方法返回之前写入。但是,对于大多数生产使用,您应该使用日志框架,而不是标准输出。Microsoft的日志接口显式不包含异步方法,因为
如果您的用例仍然要求写入标准输出,并且使用async/await的性能成本值得权衡,那么从.NET 4.5开始,TextWriter
支持WriteAsync
和WriteInAsync
方法,因此您现在可以使用:
Console.Out.WriteAsync(“…”)代码>
及
Console.Out.WriteLineAsync(“…”)代码>这是可行的,但是在线程池中启动工作对于控制台编写来说顺序语义很差。即使我从同一个线程启动workitem A和workitem B,也无法保证哪一个将首先运行。我喜欢您的建议,因为它避免了为字符串字节创建中间缓冲区。您可以通过使用线程池线程而不是创建新线程来进一步改进这一点。创建和销毁线程的成本很高。使用线程池线程可以避免这种情况@Aniket:这不会很好,因为控制台会被无序更新。保证写操作以相同的顺序进行的最好方法是使用一个单独的线程。资源消耗可以忽略不计,因为1)只创建了一个线程,2)它将永远存在。这一点很好,但不是每次都会创建一个新线程吗?var thread=new thread(blah…)告诉我,每次进入非阻塞控制台时都会创建一个新线程,对吗?@Aniket:Nah…这是一个静态构造函数,所以每个应用程序域只运行一次。你的建议值得注意,但在我的情况下是适用的
public static class NonBlockingConsole
{
private static BlockingCollection<string> m_Queue = new BlockingCollection<string>();
static NonBlockingConsole()
{
var thread = new Thread(
() =>
{
while (true) Console.WriteLine(m_Queue.Take());
});
thread.IsBackground = true;
thread.Start();
}
public static void WriteLine(string value)
{
m_Queue.Add(value);
}
}