Странная проблема в консольном приложении - PullRequest
0 голосов
/ 08 марта 2012

Редактировать: После получения пяти отрицательных голосов, но без комментариев, говорящих о том, почему, я попытался переформулировать вопрос. Я могу только предположить, что голоса были, потому что люди видят много текста и, возможно, думают, что здесь даже нет вопроса.

Я написал код, который (неправильно) ведет себя довольно странно. Кажется, код не работает так же на других компьютерах, поэтому, пожалуйста, не сердитесь на меня, если вы не можете воспроизвести проблему.

Я просто для интереса посмотрел на частоты, с которыми в GUID встречаются разные байты. Я заметил, что строковые представления направляющих всегда содержали «4». Вместо того, чтобы читать об этом в Википедии, я попытался подумать о том, что это может быть, так как может быть интересно провести небольшое «оригинальное исследование» и время от времени думать самостоятельно. (А потом прочитайте вики!)

Странная проблема на моей машине возникает, когда я пытаюсь использовать функцию "Взрыв". Если это воспроизводимо в вашей среде, вы можете воссоздать его, запустив приложение и нажав B. Это должно привести к пакету из 100 шагов (т.е. сделать 100 новых направляющих, обновляя отображаемые частоты только в конце лопнуло, потому что запись на консоль так смехотворно медленна). Но на самом деле это всего лишь один шаг!

Я установил точку останова в ProcessKey (), где назначена переменная пакета. Когда я выхожу из метода, я замечаю в выводе отладки, что поток завершается. Это не случайность; это происходит надежно каждый раз. Затем мои часы показывают мне переменную пакета, которая была просто присвоена 1000 на предыдущем шаге ... имеет значение 0.

Почему это происходит? Я сделал что-то неправильно? Я заметил, что нигде нет атрибута, определяющего STA, но я так и не понял, что это за штуки, и я ничего не удалил из шаблона консольного приложения, которое я использовал (с предварительным просмотром для разработчиков VS-2011, хотя в отличие от Redmond I живи в 2012 году сейчас) ...

Наконец: приложение перемещает курсор назад и перезаписывает текст снова и снова. Это не очень хорошо работает, если вы не можете сделать окно консоли достаточно высоким, чтобы показывать все выходные данные, поэтому вы можете поиграть с шрифтом консоли (или изменить ширину консоли, хотя приложение должно измениться соответственно Я не проверял). Шаблон становится полностью регулярным (по крайней мере, на моей машине!) С выводом из 4 столбцов (если ваша консоль имеет ширину 80 символов, вы получите 4 столбца).

Код для воспроизведения (или нет, в зависимости от обстоятельств):

using System;
using System.Diagnostics;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static bool running, exit;
        static int burst;
        static long guidCount = 0;
        static long[] counts = new long[256];
        static DateTime nextReport = DateTime.MinValue;
        static readonly TimeSpan reportInterval = TimeSpan.FromSeconds(0.25);


        static void Main(string[] args)
        {
            Console.WindowHeight = (int)(0.8*Console.LargestWindowHeight);

            WriteLine(ConsoleColor.White, "X - Exit | P - Pause | S - Step (hold for Slow) | B - Burst\r\n");
            WriteLine("Auto-reporting interval = {0}.", reportInterval);

            Guid guid;
            byte[] bytes;

            var cursorPos = new CursorLocation();

            while (!exit)
            {
                if (Console.KeyAvailable)
                {
                    ProcessKey(Console.ReadKey(true));
                }

                if (running || burst > 0)
                {
                    guid = Guid.NewGuid();
                    bytes = guid.ToByteArray();
                    ++guidCount;

                    for (int i = 0; i < 16; i++)
                    {
                        var b = bytes[i];
                        ++counts[b];
                    }

                    if (burst > 0) --burst;

                    if (burst == 0 || DateTime.Now > nextReport)
                    {
                        burst = -1;
                        cursorPos.MoveCursor();
                        ReportFrequencies();
                    }
                }
                else
                    Thread.Sleep(20);
            }
        }


        static void ProcessKey(ConsoleKeyInfo keyInfo)
        {
            switch (keyInfo.Key)
            {
                case ConsoleKey.P:
                    running = !running;
                    break;

                case ConsoleKey.B:
                    burst = 100;
                    break;

                case ConsoleKey.S:
                    burst = 1;
                    break;

                case ConsoleKey.X:
                    exit = true;
                    break;
            }
        }


        static void ReportFrequencies()
        {
            Write("\r\n{0} GUIDs generated. Frequencies:\r\n\r\n", guidCount);

            const int itemWidth = 9;
            int colCount = Console.WindowWidth / (itemWidth*2);

            for (int i = 0; i < 256; i++)
            {
                var f = (double)counts[i] / (16 * guidCount);
                Write(RightAdjust(itemWidth, "{0:x}", i));
                Write(GetFrequencyColor(f), " {0:p}".PadRight(itemWidth), f);
                if ((i + 1) % colCount == 0) Write("\r\n");
            }

            nextReport = DateTime.Now + reportInterval;
        }


        static ConsoleColor GetFrequencyColor(double f)
        {
            if (f < 0.003) return ConsoleColor.DarkRed;
            if (f < 0.004) return ConsoleColor.Green;
            if (f < 0.005) return ConsoleColor.Yellow;
            return ConsoleColor.White;
        }


        static string RightAdjust(int w, string s, params object[] args)
        {
            if (args.Length > 0)
                s = string.Format(s, args);
            return s.PadLeft(w);
        }

        #region From my library, so I need not include that here...
        class CursorLocation
        {
            public int X, Y;
            public CursorLocation()
            {
                X = Console.CursorLeft;
                Y = Console.CursorTop;
            }

            public void MoveCursor()
            {
                Console.CursorLeft = X;
                Console.CursorTop = Y;
            }
        }


        static public void Write(string s, params object[] args)
        {
            if (args.Length > 0) s = string.Format(s, args);
            Console.Write(s);
        }


        static public void Write(ConsoleColor c, string s, params object[] args)
        {
            var old = Console.ForegroundColor;
            Console.ForegroundColor = c;
            Write(s, args);
            Console.ForegroundColor = old;
        }


        static public void WriteNewline(int count = 1)
        {
            while (count-- > 0) Console.WriteLine();
        }


        static public void WriteLine(string s, params object[] args)
        {
            Write(s, args);
            Console.Write(Environment.NewLine);
        }


        static public void WriteLine(ConsoleColor c, string s, params object[] args)
        {
            Write(c, s, args);
            Console.Write(Environment.NewLine);
        }
        #endregion
    }
}

1 Ответ

1 голос
/ 09 марта 2012

Ehem. Я попытался запустить код на своем HTPC, компьютере, отличном от того, на котором я его кодировал, и теперь я не могу воспроизвести проблему. То есть, я наблюдаю пакет, приводящий только к шагу, но это происходит из-за логической ошибки в моем коде (когда интервал отчета достигнут, он устанавливает пакет в -1). Трудно поверить, что я не установил свою точку останова, прошел через нее и увидел, как переменная уничтожена, потому что я знаю, как это странно, и несколько раз пытался убедиться, что я видел то, что мне показалось. Но также трудно поверить, что я наткнулся на такую ​​странную и глубокую ошибку в framework / clr, особенно если учесть, что в моем коде была ошибка, из-за которой я сначала подключил отладчик ..

В любом случае я отмечу это как закрытое. И разместите исправленный код здесь, если кто-то хочет поиграть с ним. Я исправил ошибку и сделал вывод немного более компактным, чтобы он работал лучше на менее щедрых экранах, чем 22-дюймовый full-HD_one, на котором я это делал. Теперь он использует 8 столбцов независимо от ширины консоли, что, вероятно, является безопасным предположением. что большинство людей используют стандартную ширину 80 символов, в которую теперь помещаются 8 столбцов.

Если кому-то захочется запустить это и опубликовать свои выводы (просто нажмите P, чтобы быстро получить стабильные частоты, шаг / всплеск предназначен для глупых вещей, таких как просмотр распределения через меньшее количество поколений). На моем HTPC я получаю такой результат:

0x00 - 0x3f  0.34%
0x40 - 0x4f  0.73%
0x50 - 0x7f  0.34%
0x80 - 0xbf  0.44%
0xc0 - 0xff  0.34%

Это означает: байты от 0x00 до 0x3f составляют 0,34% от всех байтов во всех сгенерированных направляющих (509 194 в данном конкретном случае, но я получаю этот результат каждый раз с более чем 100 000 направляющих или около того). Есть 3 очень разные группы, и, может быть, если я сейчас пойду и прочту о гидах в Википедии, я пойму, почему это так. Но было бы не очень весело делать это, если бы мое «открытие» было тем, что я знал до того, как начал. :)

using System;
using System.Diagnostics;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
        static bool running, exit;
        static int burst;
        static long guidCount = 0;
        static long[] counts = new long[256];
        static DateTime nextReport = DateTime.MinValue;
        static readonly TimeSpan reportInterval = TimeSpan.FromSeconds(1);


        static void Main(string[] args)
        {
            Console.WindowHeight = (int)(0.8 * Console.LargestWindowHeight);

            WriteLine(ConsoleColor.White, "X - Exit | P - Run/Pause | S - Step (hold for Slow) | B - Burst");
            WriteLine("Press P, S or B to make something happen.", reportInterval);

            Guid guid;
            byte[] bytes;

            var cursorPos = new CursorLocation();

            while (!exit)
            {
                if (Console.KeyAvailable)
                {
                    ProcessKey(Console.ReadKey(true));
                }

                if (running || burst > 0)
                {
                    guid = Guid.NewGuid();
                    bytes = guid.ToByteArray();
                    ++guidCount;

                    for (int i = 0; i < 16; i++)
                    {
                        var b = bytes[i];
                        ++counts[b];
                    }

                    if (burst > 0) --burst;

                    if (burst == 0 && DateTime.Now > nextReport)
                    {
                        cursorPos.MoveCursor();
                        ReportFrequencies();
                    }
                }
                else
                    Thread.Sleep(20);
            }
        }


        static void ProcessKey(ConsoleKeyInfo keyInfo)
        {
            switch (keyInfo.Key)
            {
                case ConsoleKey.P:
                    running = !running;
                    break;

                case ConsoleKey.B:
                    burst = 100;
                    break;

                case ConsoleKey.S:
                    burst = 1;
                    break;

                case ConsoleKey.X:
                    exit = true;
                    break;
            }
        }


        static void ReportFrequencies()
        {
            Write("\r\n{0} GUIDs generated. Frequencies (%):\r\n\r\n", guidCount);

            const int itemWidth = 11;
            const int colCount = 8; // Console.WindowWidth / (itemWidth + 2);

            for (int i = 0; i < 256; i++)
            {
                var f = (double)counts[i] / (16 * guidCount);
                var c = GetFrequencyColor(f);
                Write(c, RightAdjust(3, "{0:x}", i));
                Write(c, " {0:0.00}".PadRight(itemWidth), f*100);
                if ((i + 1) % colCount == 0) Write("\r\n");
            }

            nextReport = DateTime.Now + reportInterval;
        }


        static ConsoleColor GetFrequencyColor(double f)
        {
            if (f < 0.003) return ConsoleColor.DarkRed;
            if (f < 0.004) return ConsoleColor.Green;
            if (f < 0.005) return ConsoleColor.Yellow;
            return ConsoleColor.White;
        }


        static string RightAdjust(int w, string s, params object[] args)
        {
            if (args.Length > 0)
                s = string.Format(s, args);
            return s.PadLeft(w);
        }

        #region From my library, so I need not include that here...
        class CursorLocation
        {
            public int X, Y;
            public CursorLocation()
            {
                X = Console.CursorLeft;
                Y = Console.CursorTop;
            }

            public void MoveCursor()
            {
                Console.CursorLeft = X;
                Console.CursorTop = Y;
            }
        }


        static public void Write(string s, params object[] args)
        {
            if (args.Length > 0) s = string.Format(s, args);
            Console.Write(s);
        }


        static public void Write(ConsoleColor c, string s, params object[] args)
        {
            var old = Console.ForegroundColor;
            Console.ForegroundColor = c;
            Write(s, args);
            Console.ForegroundColor = old;
        }


        static public void WriteNewline(int count = 1)
        {
            while (count-- > 0) Console.WriteLine();
        }


        static public void WriteLine(string s, params object[] args)
        {
            Write(s, args);
            Console.Write(Environment.NewLine);
        }


        static public void WriteLine(ConsoleColor c, string s, params object[] args)
        {
            Write(c, s, args);
            Console.Write(Environment.NewLine);
        }
        #endregion
    }
}

Опубликуйте ваши результаты, дамы и господа. :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...