我正在编写控制台应用程序,它通过调度程序执行一些工作并将输出写入控制台。一切都很好但是当我点击控制台时它停止工作并等待我的右键单击。之后它继续工作。
我认为它根本不会将文本写入控制台并执行它需要执行的操作但不会等待我的交互。我可以将此代码重写为WinForms或WPF,但我认为它可以用另一种方式解决。这是我的代码
static void Main(string[] args)
{
Console.WriteLine("Started...");
var timer = new System.Timers.Timer(1000);
timer.Elapsed += timer_Elapsed;
timer.Start();
Console.ReadLine();
}
static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Writing to file " + DateTime.Now.ToString());
System.IO.File.AppendAllLines(@"C:\Temp\log.txt",
new[] { DateTime.Now.ToString()});
}
单击控制台后,它会停止将时间附加到文件log.txt。 任何想法如何解决?感谢。
答案 0 :(得分:6)
这是标准的控制台行为,它等待用户输入锁定执行线程。
要理解为什么要看Console.Write
实现。它只是写入控制台输出(Console.Out
属性),默认情况下是同步TextWriter
(source)。所以,“神奇”就在这里。
您可以通过将控制台写入委托给专用线程来解决此问题。比如他的方式:
static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
Task.Factory.StartNew(() =>
{
Console.WriteLine("Writing to file " + DateTime.Now.ToString());
});
System.IO.File.AppendAllLines(@"C:\Temp\log.txt",
new[] { DateTime.Now.ToString()});
}
因此,您的应用程序将继续写入文件,但只有在您右键单击时才会将文本写入控制台。 这个实现的问题是它可以创建很多增加ThreadPool的线程。
可以使用一些特殊的TaskScheduler(例如SequentialScheduler
)来实现更好的实现 static TaskFactory factory = new TaskFactory(new SequentialScheduler());
static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
factory.StartNew(() =>
{
Console.WriteLine("Writing to file " + DateTime.Now.ToString());
});
System.IO.File.AppendAllLines(@"C:\Temp\log.txt",
new[] { DateTime.Now.ToString()});
}
它不会增加ThreadPool。也可以使用另一种实现,但主要思想是 - 将控制台写入委托给另一个可能被用户阻止的线程,工作线程将被解除阻塞并继续工作。
答案 1 :(得分:3)
有关实施的建议
如果你想避免在线程池上投入大量工作,只是为了将一些东西写入控制台,那么队列就是你的朋友。这也确保了消息的正确顺序,并为您提供了一些额外的控制(比如处理不重要的条目)。
创建一个控制台记录程序线程,该线程读取并行队列,在该队列中将条目排入队列以写入控制台。请注意,如果控制台被无限期阻塞,那么队列最终会增长,直到你的内存不足为止 - 也就是说,如果你确实在那段时间内将数百万条目排入队列。
示例:强>
static ConcurrentQueue<string> consoleQueue = new ConcurrentQueue<string>();
static ManualResetEventSlim itemEnqueuedEvent = new ManualResetEventSlim();
static void WriteToConsoleLoop(object state)
{
var token = (CancellationToken)state;
while (!token.IsCancellationRequested)
{
string entry;
while (consoleQueue.TryDequeue(out entry))
{
Console.WriteLine(entry);
}
try
{
itemEnqueuedEvent.Wait(token);
itemEnqueuedEvent.Reset();
}
catch (OperationCanceledException)
{
break;
}
}
}
static void WriteToConsole(string entry)
{
consoleQueue.Enqueue(entry);
itemEnqueuedEvent.Set();
}
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
// Background or foreground, depends on how vital it is
// to print everything in the queue before shutting down.
var thread = new Thread(WriteToConsoleLoop) { IsBackground = true };
thread.Start(cts.Token);
WriteToConsole("Started...");
// Do your stuff...
cts.Cancel();
}