jeudi 7 février 2019

Strange behavior with multithreading and random

I wrote a "gimmick" program that is supposed to display random chars with random colors (For- and Background) in a Console-Window using Math.Random() and Multithreading. For more randomness I didn't made the program "thread safe". (Additional Information: I originally wanted the program to display an Space-Invader in the center, I achieved that with Thread-Safety, and I know, multithreading is supposed to be thread safe but this is not what this question is about)

The output looks like this:

The expected Output

The function of the program is like that: I have an Array in which all the Positions (X/Y) with colors and Char are stored. I have some Functions that change this array and I have some functions to display the array. I also got a function to return random chars and one to return random colors.

Now the point that I don't get: Sometimes everything works as described, but sometimes the program starts to display only !-Chars (exclamation mark) but keeps the random colors and positions:

Random colors with exclamation mark only

Another time the program only shows the colors black and white but the chars keep being random:

Black and white with random chars

And sometimes it happens, that the program only displays !-Chars and only black and white:

Black and white with exclamation mark only

What I could say is the following:

My "Get a Random Char" function looks like that:

public static char GetChar()
{
    return (char)randomChar.Next(33, 150);
}

!-Char is Ascii-Char 33. That means if the Program get stucked the Random-Char-Function only returns the lowest Random-Char (== 33 == !)

I got something similar for the colors. I give an random-number between 0 and 16 to a function to get back a Console-Color:

private ConsoleColor SetColor(char ColorIndex)
{
    switch (ColorIndex)
    {
        case (char)1:
            return ConsoleColor.Black;
        case (char)2:
            return ConsoleColor.Blue;
        case (char)3:
            return ConsoleColor.Cyan;
        case (char)4:
            return ConsoleColor.DarkBlue;
        case (char)5:
            return ConsoleColor.DarkCyan;
        case (char)6:
            return ConsoleColor.DarkGray;
        case (char)7:
            return ConsoleColor.DarkGreen;
        case (char)8:
            return ConsoleColor.DarkMagenta;
        case (char)9:
            return ConsoleColor.DarkRed;
        case (char)10:
            return ConsoleColor.DarkYellow;
        case (char)11:
            return ConsoleColor.Gray;
        case (char)12:
            return ConsoleColor.Green;
        case (char)13:
            return ConsoleColor.Magenta;
        case (char)14:
            return ConsoleColor.Red;
        case (char)15:
            return ConsoleColor.White;
        case (char)16:
            return ConsoleColor.Yellow;
        default:
            return ConsoleColor.Black;
    }
}

//The call looks like that:
_Data[x, y, 1] = (char)random.Next(0, 16);

I know that random.Next(0, 16) will give a 15 as maximum number back, and 15 is the color white in the example. If I change 15 to red, the Program will display red instead of white, when the program gets stucked:

If white is changed to red

This means: When the program got stucked the Random-Char-Function always returns the lowest possible number (33) and the Random-Color-Function always returns the highest possible number (15).

The question: Why is this behaviour? Why is it just sometimes and not every time? And why it is every time after a different time of running? Why is one random function always returning max-number and the other one always the min-number?

My guess is, that his is because of the CPU predicting, but as I said, it's just a guess and I wonder how this works, and why it goes to different approaches (min/max).

Here are some of my Code, if needed I could post more Code:

//Program-Start after initialising some stuff:
public AnotherOne()
{
    new Thread(DrawData).Start();
    new Thread(DrawDataLR).Start();
    new Thread(DrawDataRL).Start();
    new Thread(DrawDataTD).Start();
    new Thread(DrawDataDT).Start();
    new Thread(ChangeData).Start();
    ChangeData();
}

//Draw Data example for Left-Right:
private void DrawDataLR()
{
    while (!_done)
    {
        DrawDataLeftRight();
        Thread.Sleep(2);
    }
}

private void DrawDataLeftRight()
{
    for (int x = 0; x < _Width - 1; x++)
    {
        for (int y = 0; y < _Height - 1; y++)
        {
            Console.SetCursorPosition(x, y);
            Console.BackgroundColor = SetColor(_Data[x, y, 1]);
            Console.ForegroundColor = SetColor(_Data[x, y, 2]);
            Console.WriteLine(_Data[x, y, 0]);
        }
    }
}

//Draw Data Function:
private void DrawData()
{
    Random next = new Random();
    int x = 1;

    while (!_done)
    {
        x = next.Next(1, 5);

        switch (x)
        {
            case 1:
                DrawDataLeftRight();
                break;
            case 2:
                DrawDataTopDown();
                break;
            case 3:
                DrawDataRightLeft();
                break;
            case 4:
                DrawDataDownTop();
                break;
        }
    }
}

//Change Data Function with "stripes" as Example:
private void ChangeData()
{
    int x = 100;

    while (!_done)
    {
        FillRandomFeld();
        Thread.Sleep(x);
        ClearChar();
        Thread.Sleep(x);
        SetColor();
        Thread.Sleep(x);
        Stripes();
        Thread.Sleep(x);
        SetChar();
        Thread.Sleep(x);
        OtherStripes();
        Thread.Sleep(x);

        x = randomX.Next(0, 100);
    }
}

private void Stripes()
{
    char colr = (char)random.Next(0, 16);

    for (int x = 0; x < _Width - 1; x += 4)
    {
        for (int y = 0; y < _Height - 1; y++)
        {
            if (_Data[x, y, 3] != (char)1)
            {
                _Data[x, y, 1] = colr;
                _Data[x, y, 2] = colr;
            }
        }
    }
}




Aucun commentaire:

Enregistrer un commentaire