多线程和随机奇怪的行为

问题描述 投票:-1回答:2

编辑@ Dublicate:我知道这是不推荐的不安全使用线程的。我的问题是关于为什么,不是随机的,如果是线程安全的。多亏了答案,帮助我更好地理解了!

我写应该显示使用的Math.random()和多线程在一个控制台窗口随机颜色(有关─和背景)随机字符一个“噱头”程序。对于更多的随机性我没有制作的节目“线程安全的”。 (附加信息:我本来想节目在中央显示的空间侵略者,我实现了与线程安全的,我知道,多线程应该是线程安全的,但是这是不是这个问题是关于什么的)

输出看起来是这样的:

The expected Output

该程序的功能是这样的:我具有其中所有的位置(X / Y)颜色和字符被存储的数组。我有一些函数改变这种阵列与我有一些功能来显示阵列。我也有一个函数来返回随机字符和一个返回随机颜色。

现在,我不明白这一点:有时候,一切都将按所描述的,但有时在程序启动只显示-chars(惊叹号),但保持了随机颜色和位置!

Random colors with exclamation mark only

还有一次节目只显示黑色和白色,但字符被保留随机:

Black and white with random chars

而且有时会发生,该程序只显示-chars只有黑色和白色!

Black and white with exclamation mark only

我可以说的是以下几点:

我“得到一个随机字符”的功能看起来像这样:

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

!-Char是ASCII码字符33,如果计划中获得stucked随机炭火功能只返回最低的随机字符(== == 33!)指

我得到的颜色类似的东西。我给0到16之间的随机数的函数来得到一个控制台,颜色:

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);

我知道random.Next(0,16)将给出15作为最大数目背面,15是彩色的白色中的示例。如果我改变15为红色,程序会显示红色的,而不是白色的,当程序被stucked:

If white is changed to red

这意味着:当项目被stucked随机字符,函数总是返回尽可能少的(33)和随机颜色函数总是返回最大可能的数目(15)。

问题:为什么这种行为?为什么只是有时并不是每一次?为什么它是运行的不同的时间后,每一次?为什么一个随机函数总是返回最大数,另一个总是分钟数?

我的猜测是,他是因为CPU预测的,但正如我所说,这只是一个猜测,我不知道这是如何工作的,为什么它会以不同的方法(最小/最大)。

下面是我的一些代码,如果需要的话我可以发布更多的代码:

//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;
            }
        }
    }
}
c# multithreading random console prediction
2个回答
1
投票

Random.Next()结束调用这个代码(从the reference source):

  private int InternalSample() {
      int retVal;
      int locINext = inext;
      int locINextp = inextp;

      if (++locINext >=56) locINext=1;
      if (++locINextp>= 56) locINextp = 1;

      retVal = SeedArray[locINext]-SeedArray[locINextp];

      if (retVal == MBIG) retVal--;          
      if (retVal<0) retVal+=MBIG;

      SeedArray[locINext]=retVal;

      inext = locINext;
      inextp = locINextp;

      return retVal;
  }

看着这个代码,很明显,多线程访问可以做一些非常糟糕的事情。例如,如果inextinextp最终含有相同的值,然后SeedArray[locINext]-SeedArray[locINextp];将总是导致0,Random.Next()将总是返回0。

我敢肯定,你可以开始想象很多其他的方法是多线程访问该代码可以把事情搞得一团糟。

从概念上讲,在内部的PRNG具有大数量的状态,其中每个状态对应于PRNG的可能的输出(虽然多个状态可以有相同的输出),并且它们通过这些状态中的每一个以确定的顺序行走。如果你破坏PRNG的内部状态,可以循环回第一个,所以你得到一个小的重复设置“随机”输出数字之前开始通过更小的组的那些国家的行走。

在代码中的另一个地方,你在每一个方法被调用时创建一个新的Random实例。作为链接的答复中提到,这将导致许多Random情况下,它会返回同一组随机数字。

埃里克利珀有大约提高Random类正在进行的系列博客文章:part 1 part 2(和更多的惊喜)。


1
投票

你的多线程使用随机对象被破坏其内部状态。

您可以重现该问题很容易使用下面的程序。如果你运行它,一段时间后(或有时,立即)将开始生产零,而不是随机数。您可能需要运行几次才能看到效果。此外,它出错更经常一个发布版本,而不是调试版本(这是不寻常的多线程问题!)。

(注意:这是一个16芯处理器使用。NET 4.7.2测试结果可用于其他系统而有所不同。)。

using System;
using System.Threading.Tasks;

namespace Demo
{
    class Program
    {
        static void Main()
        {
            Console.WriteLine();

            Random rng = new Random(12345);

            Parallel.Invoke(
                () => printRandomNumbers(rng),
                () => printRandomNumbers(rng),
                () => printRandomNumbers(rng));

            Console.ReadLine();
        }

        static void printRandomNumbers(Random rng)
        {
            while (rng.Next() != 0)
            {}

            while (true)
            {
                Console.WriteLine(rng.Next());
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.