11

我编写了一个控制台应用程序,它使用 console.write 和 console.writeline 来提供一些日志记录。该应用程序是一个服务器应用程序,它使用异步 beginacceptconnection() 和 beginread() ( Sockets ) 进行通信。有时我会收到有关它挂起的报告,并且从有限的调试中我可以看到问题是 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 被打断吗?...我猜它打断了它...但我真的不知道!

任何帮助我都会非常感激。

问候,

戈登。

4

4 回答 4

8

我有同样的问题。

console.readkey()在主线程中使用以防止在调试模式下关闭应用程序。

在我用无限循环替换它后,我的问题就解决了。

于 2012-09-11T13:52:19.990 回答
2

您应该通过删除日志记录周围的锁来解决您的问题。日志记录是通过Console.WriteLine同步的(和线程安全的)完成的。您可能通过自己的锁定机制导致死锁(尽管我无法在没有看到代码的情况下进行验证)。

于 2011-06-27T20:52:41.013 回答
2

我猜你的应用程序是由另一个重定向标准错误和标准输出的进程启动的。如果您的“观察者”进程对同一线程上的两个流使用ReadToEnd() ,您可能会死锁。

死锁的另一种选择是通过标准输入发送子进程输入,这反过来又启动另一个进程,该进程有一个无限期地等待输入的控制台。这在我身上发生过一次 wmic.exe,它在 stdin 被重定向时会阻塞。

如果你玩过你的日志类,我怀疑你用你自己的改变了底层的 Console.Out 流。请至少发布您的应用程序挂起的调用堆栈,以便我们进行分析。如果您用自己的控制台流替换控制台流,那么有很多方法可以让您自责。

你的,阿洛伊斯克劳斯

于 2011-06-27T20:53:07.550 回答
2

这有点远,但我想知道您是否使用 ToString() 方法需要锁定的对象调用 Console.WriteLine。如果是这样,您可能会因 Console.WriteLine 内部获取的锁而陷入死锁状态。

我曾经将此错误报告发布到 Microsoft Connect,但遗憾的是他们拒绝修复它。

于 2011-06-27T22:17:20.750 回答