使用 Console.Writeline() 或 Console.Write() 时,多线程 C# 控制台应用程序中很少会挂起
我编写了一个控制台应用程序,它使用 console.write 和 console.writeline 来提供一些日志记录。该应用程序是一个服务器应用程序,使用异步 beginacceptconnection() 和 beginread() (套接字)进行通信。有时我会收到有关它挂起的报告,并且从我可以做的有限调试中我可以看到问题是 Console.Writeline() 或 Console.write()。
作为多线程,我一直小心地在日志记录类周围加一把锁,这样只有一个线程可以一次记录一条消息......当我发现挂起时,我得到的只是锁上的线程阻塞和 VS 报告控件已传递到 Console.Write 并且正在等待它返回......但它永远不会返回。
几天前,我收到了另一份失败报告,但这次是在启动期间......尚未启动异步连接(尽管主线程确实生成了一个启动线程),并且向我发送了一张图片...... ...见下文。(我添加了开始和结束关键部分行来防止这种情况发生,但它没有)
// Logging Class
public class Logging
{
// Lock to make the logging class thread safe.
static readonly object _locker = new object();
public delegate void msgHandlerWriteLineDelegate(string msg, Color col);
public static event msgHandlerWriteLineDelegate themsgHandlerWriteLineDelegate;
public delegate void msgHandlerWriteDelegate(string msg, Color col);
public static event msgHandlerWriteDelegate themsgHandlerWriteDelegate;
public static void Write(string a, Color Col)
{
if (themsgHandlerWriteDelegate != null)
{
lock (_locker)
{
themsgHandlerWriteDelegate(a, Col);
}
}
}
public static void Write(string a)
{
if (themsgHandlerWriteDelegate != null)
{
lock (_locker)
{
themsgHandlerWriteDelegate(a, Color.Black);
}
}
}
public static void WriteLine(string a, Color Col)
{
if (themsgHandlerWriteLineDelegate != null)
{
lock (_locker)
{
themsgHandlerWriteLineDelegate(a, Col);
}
}
}
public static void WriteLine(string a)
{
if (themsgHandlerWriteLineDelegate != null)
{
lock (_locker)
{
themsgHandlerWriteLineDelegate(a, Color.Black);
}
}
}
// Console Methods That implement the delegates in my logging class.
public static void ConsoleWriteLine(string message, Color Col)
{
try
{
if (Col == Color.Black)
{
Console.ForegroundColor = ConsoleColor.Gray;
}
else
{
Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), Col.Name);
}
Thread.BeginCriticalRegion();
Console.WriteLine(message);
Thread.EndCriticalRegion();
Console.ForegroundColor = ConsoleColor.Gray;
}
catch (ThreadAbortException ex)
{
Console.WriteLine("ThreadAbortException : " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Exception : " + ex.Message);
}
}
public static void ConsoleWrite(string message, Color Col)
{
try
{
if (Col == Color.Black)
{
Console.ForegroundColor = ConsoleColor.Gray;
}
else
{
Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), Col.Name);
}
Thread.BeginCriticalRegion();
Console.Write(message);//**THIS IS WHERE IS HANGS...IT NEVER RETURNS **
Thread.EndCriticalRegion();
Console.ForegroundColor = ConsoleColor.Gray;
}
catch (ThreadAbortException ex)
{
Console.WriteLine("ThreadAbortException : " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Exception : " + ex.Message);
}
}
public static void ConsoleUpdate(string message)
{
try
{
Thread.BeginCriticalRegion();
Console.WriteLine(message);//**THIS IS WHERE IS HANGS...IT NEVER RETURNS **
Thread.EndCriticalRegion();
}
catch (ThreadAbortException ex)
{
Console.WriteLine("ThreadAbortException : " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Exception : " + ex.Message);
}
}
// The main method...subscribes to delegates and spawns a thread to boot HW..main thread then exits.
public static void Main()
{
Logging.themsgHandlerWriteDelegate += new Logging.msgHandlerWriteDelegate(ConsoleWrite);
Logging.themsgHandlerWriteLineDelegate += new Logging.msgHandlerWriteLineDelegate(ConsoleWriteLine);
Logging.themsgHandlerUpdateDelegate += new Logging.msgHandlerUpdateDelegate(ConsoleUpdate);
}
}
public class ClassOnOtherThread
{
// In a different class running on a different thread the following line occasionly invokes the error:
private void BootHw(string Resource, string Resource2)
{
Logging.Write("\t\t[");
}
}
我对 MSDN 的阅读表明 Console.WriteLine 和 Console.Write 是线程安全的,因此我实际上不需要锁这……我也不敢相信微软的代码是错误的(;-),所以我猜测是我的代码正在执行的一些交互导致了错误。
现在我的问题是:我应该做些什么来防止 Console.WriteLine 和 Console.Write 被中断吗?...我猜测它会中断它...但我真的不知道!
任何帮助我将非常感激。
问候,
戈登。
I have written a console application that makes use of console.write and console.writeline to provide some logging. The application is a server application that uses asynchronous beginacceptconnection() and beginread() ( Sockets ) for communication. Occasionally i get reports of it hanging and from the limited debug i can do i am able to see the problem being Console.Writeline() or Console.write().
Being multi-threaded I have been careful to have a lock around the logging class so only one thread can log a message at once.....when I've caught a hang all i get are threads blocking on the lock and VS reporting that the control has passed into Console.Write and it is waiting for it to come back....it never does.
A couple of days ago i got another report of a failure but this time during bootup....where no asynch connections have yet been kicked off( the main thread does spawn a thread to bootup though ) and I was sent a picture.....see below.( i added the begin and end critical section lines to prevent this and it did not )
// Logging Class
public class Logging
{
// Lock to make the logging class thread safe.
static readonly object _locker = new object();
public delegate void msgHandlerWriteLineDelegate(string msg, Color col);
public static event msgHandlerWriteLineDelegate themsgHandlerWriteLineDelegate;
public delegate void msgHandlerWriteDelegate(string msg, Color col);
public static event msgHandlerWriteDelegate themsgHandlerWriteDelegate;
public static void Write(string a, Color Col)
{
if (themsgHandlerWriteDelegate != null)
{
lock (_locker)
{
themsgHandlerWriteDelegate(a, Col);
}
}
}
public static void Write(string a)
{
if (themsgHandlerWriteDelegate != null)
{
lock (_locker)
{
themsgHandlerWriteDelegate(a, Color.Black);
}
}
}
public static void WriteLine(string a, Color Col)
{
if (themsgHandlerWriteLineDelegate != null)
{
lock (_locker)
{
themsgHandlerWriteLineDelegate(a, Col);
}
}
}
public static void WriteLine(string a)
{
if (themsgHandlerWriteLineDelegate != null)
{
lock (_locker)
{
themsgHandlerWriteLineDelegate(a, Color.Black);
}
}
}
// Console Methods That implement the delegates in my logging class.
public static void ConsoleWriteLine(string message, Color Col)
{
try
{
if (Col == Color.Black)
{
Console.ForegroundColor = ConsoleColor.Gray;
}
else
{
Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), Col.Name);
}
Thread.BeginCriticalRegion();
Console.WriteLine(message);
Thread.EndCriticalRegion();
Console.ForegroundColor = ConsoleColor.Gray;
}
catch (ThreadAbortException ex)
{
Console.WriteLine("ThreadAbortException : " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Exception : " + ex.Message);
}
}
public static void ConsoleWrite(string message, Color Col)
{
try
{
if (Col == Color.Black)
{
Console.ForegroundColor = ConsoleColor.Gray;
}
else
{
Console.ForegroundColor = (ConsoleColor)Enum.Parse(typeof(ConsoleColor), Col.Name);
}
Thread.BeginCriticalRegion();
Console.Write(message);//**THIS IS WHERE IS HANGS...IT NEVER RETURNS **
Thread.EndCriticalRegion();
Console.ForegroundColor = ConsoleColor.Gray;
}
catch (ThreadAbortException ex)
{
Console.WriteLine("ThreadAbortException : " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Exception : " + ex.Message);
}
}
public static void ConsoleUpdate(string message)
{
try
{
Thread.BeginCriticalRegion();
Console.WriteLine(message);//**THIS IS WHERE IS HANGS...IT NEVER RETURNS **
Thread.EndCriticalRegion();
}
catch (ThreadAbortException ex)
{
Console.WriteLine("ThreadAbortException : " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Exception : " + ex.Message);
}
}
// The main method...subscribes to delegates and spawns a thread to boot HW..main thread then exits.
public static void Main()
{
Logging.themsgHandlerWriteDelegate += new Logging.msgHandlerWriteDelegate(ConsoleWrite);
Logging.themsgHandlerWriteLineDelegate += new Logging.msgHandlerWriteLineDelegate(ConsoleWriteLine);
Logging.themsgHandlerUpdateDelegate += new Logging.msgHandlerUpdateDelegate(ConsoleUpdate);
}
}
public class ClassOnOtherThread
{
// In a different class running on a different thread the following line occasionly invokes the error:
private void BootHw(string Resource, string Resource2)
{
Logging.Write("\t\t[");
}
}
My reading of the MSDN suggests Console.WriteLine and Console.Write are threadsafe and therefore i don't actually need a lock around it....i also can't believe it Microsoft's code is wrong(;-) and so I am guessing it is some interaction my code is doing which creates the error.
Now my question : Should i be doing anything to prevent Console.WriteLine and Console.Write being interrupted?...it is my guess that something it interrupting it...but i don't really know that!!
Any help would me very much appreciated.
Regards,
Gordon.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我也有同样的问题。
我在主线程中使用console.readkey()来防止在调试模式下关闭应用程序。
当我用无限循环替换它后,我的问题就解决了。
I had the same problem.
I was using
console.readkey()
in main thread to prevent closing application in debug mode.After I replaced it with an infinite loop my problem was solved.
您应该通过删除日志记录周围的锁来解决您的问题。日志记录是通过同步(并且线程安全)的Console.WriteLine 完成的。您可能通过自己的锁定机制导致死锁(尽管我无法在没有看到代码的情况下进行验证)。
You should solve your problem by removing the locks around the logging. The logging is done via
Console.WriteLine
which is synchronized (and thread safe). You are possibly causing a deadlock through your own locking mechanism (though I can't verify without seeing the code).我猜您的应用程序是由另一个进程启动的,该进程会重定向 stderr 和 stdout。如果您的“观察者”进程使用 ReadToEnd() 对于同一线程上的两个流,您可能会发生死锁。
死锁的另一个选择是通过 stdin 发送子进程输入,这又会启动另一个进程,该进程有一个无限期等待输入的控制台。我在 wmic.exe 中发生过一次这种情况,当 stdin 重定向时它会阻塞。
如果您使用过日志类,我怀疑您用自己的日志类更改了 Console.Out 的底层流。请至少发布您的应用程序挂起的调用堆栈,以便我们可以分析一些内容。如果您用自己的控制台流替换控制台流,那么有很多方法会搬起石头砸自己的脚。
你的,
阿洛伊斯·克劳斯
I guess your application is started by another process which does redirect stderr and stdout. If your "watcher" process uses ReadToEnd() for both streams on the same thread you can deadlock.
Another option to deadlock is to send the child process input via stdin which in turn starts another process which has a console which waits for input indefinitely. This happend once to me with wmic.exe which does block when stdin is redirected.
If you have played with your log class I suspect that you change the underlying Stream of Console.Out with your own one. Please post at least the callstack where your application is hanging so we have something to analyze. There are many ways to shoot yourself in the foot if you replace the console stream with your own one.
Yours,
Alois Kraus
这有点遥远,但我想知道您是否正在使用 ToString() 方法获取锁定的对象调用 Console.WriteLine。如果是这样,您可能会因 Console.WriteLine 内部获取的锁而陷入死锁情况。
我曾经发布过 此错误报告给 Microsoft Connect,但遗憾的是他们拒绝修复它。
This is a bit of a long shot, but I wonder if you are calling Console.WriteLine with objects whose ToString() method takes a lock. If so, you can get yourself in a deadlock situation with respect to the lock taken internally by Console.WriteLine.
I once posted this bug report to Microsoft Connect, though sadly they declined to fix it.