Редкие зависания в многопоточном консольном приложении C # при использовании Console.Writeline () или Console.Write () - PullRequest
9 голосов
/ 28 июня 2011

Я написал консольное приложение, которое использует 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 - потокобезопасный, и поэтому мне на самом деле не нужна блокировка вокруг него .... я также не могу поверить в то, что код Microsoft неправильный (;-), и поэтому я предполагаю, что мой код выполняет какое-то взаимодействие, которое создаетошибка.

Теперь мой вопрос: должен ли я что-то делать, чтобы предотвратить прерывание Console.WriteLine и Console.Write? ... я предполагаю, что что-то прерывает его ... но я недействительно знаю, что !!

Любая помощь была бы мне очень признательна.

С уважением,

Гордон.

Ответы [ 5 ]

8 голосов
/ 11 сентября 2012

У меня была такая же проблема.

Я использовал console.readkey() в главном потоке, чтобы предотвратить закрытие приложения в режиме отладки.

После того, как я заменил его на бесконечный цикл, моя проблема была решена.

2 голосов
/ 28 июня 2011

Это немного, но мне интересно, если вы вызываете Console.WriteLine с объектами, чей метод ToString () принимает блокировку.Если это так, вы можете оказаться в тупиковой ситуации с блокировкой, принятой внутри Console.WriteLine.

Однажды я опубликовал этот отчет об ошибке в Microsoft Connect, хотя, к сожалению, они отказалисьисправить это.

2 голосов
/ 28 июня 2011

Вы должны решить свою проблему, сняв замки вокруг регистрации. Регистрация ведется через Console.WriteLine, который синхронизирован (и потокобезопасен). Возможно, вы вызываете взаимоблокировку через свой собственный механизм блокировки (хотя я не могу проверить, не увидев код).

2 голосов
/ 28 июня 2011

Consol.Writeline() является поточно-ориентированным. Так

Я был осторожен, чтобы заблокировать класс регистрации

Это не должно быть необходимым и вполне может быть причиной тупиков. Без примеров кода невозможно сказать, если / как / что.

и, таким образом, я предполагаю, что мой код выполняет какое-то взаимодействие, которое создает ошибку.

Вполне вероятно.

это мое предположение, что что-то прерывает это ..

номер

1 голос
/ 28 июня 2011

Полагаю, ваше приложение запущено другим процессом, который перенаправляет stderr и stdout. Если ваш процесс-наблюдатель использует ReadToEnd () для обоих потоков в одном потоке, вы можете зайти в тупик.

Другой вариант взаимоблокировки - отправка входных данных дочернего процесса через stdin, который, в свою очередь, запускает другой процесс, имеющий консоль, которая ожидает ввода бесконечно. Это случилось однажды со мной с wmic.exe, который блокируется при перенаправлении stdin.

Если вы играли со своим классом журналов, я подозреваю, что вы изменили базовый поток консоли. Выход со своим собственным. Пожалуйста, опубликуйте хотя бы callstack, где висит ваше приложение, чтобы у нас было что проанализировать. Есть много способов выстрелить себе в ногу, если вы замените поток консоли своим собственным.

С уважением, Алоис Краус

...