在C#上使用多线程的问题

Cla*_*dia 4 c# console recursion multithreading

我想实现一个程序,显示一些在控制台上随机移动的字符,每个字符都有不同的速度.

我创建了一个递归方法,在控制台上随机移动一个字母.当我想移动两个字母时,我使用两个线程调用相同的方法.

该程序在第一分钟完美运行,但过了一段时间后,这些字母开始在控制台上出现!

我确定我的递归方法没问题(我甚至尝试创建另一种方法,这次只使用一段时间(i <100000)而不是递归.但出现了同样的错误).有人可以帮我吗?

非常感谢你.

编辑:对不起,这是一个示例代码(不考虑如果字母占据相同的位置会发生什么).字母在"体育场"上移动,它们在x轴上移动20-51,在y轴上移动5-26.

public void WriteAt(string s, int x, int y)
    {
        try
        {
            Console.SetCursorPosition(x, y);
            Console.Write(s);
        }
        catch (ArgumentOutOfRangeException e)
        {
            Console.Clear();
            Console.WriteLine(e.Message);
        }

    }

    public void impresion()
    {
        int x = random.Next(20, 51);
        int y = random.Next(5, 26);
        WriteAt("A", x, y);
        imprimir("A", x, y, 80);
    }

    public void impresion2()
    {
        int x = random.Next(20, 51);
        int y = random.Next(5, 26);
        WriteAt("E", x, y);
        imprimir2("E", x, y, 20);
    }

    public void go()
    {
        Thread th1 = new Thread(impresion);
        Thread th2 = new Thread(impresion2);
        th1.Start(); //creates an 'A' that will move randomly on console
        th2.Start(); //creates an 'E' that will move randomly on console
    }

    public void imprimir(string s, int x, int y, int sleep)
    {
        Thread.Sleep(sleep);
        WriteAt(" ", x, y);
        int n = random.Next(1, 5);

        if (n == 1)
        {
            if ((x + 1) > 50)
            {
                WriteAt(s, x, y);
                imprimir(s, x, y, sleep);
            }
            else
            {
                WriteAt(s, x + 1, y);
                imprimir(s, x + 1, y, sleep);
            }
        }

        else if (n == 2)
        {
            if ((y - 1) < 5)
            {
                WriteAt(s, x, y);
                imprimir(s, x, y, sleep);
            }
            else
            {
                WriteAt(s, x, y - 1);
                imprimir(s, x, y - 1, sleep);
            }
        }

        else if (n == 3)
        {
            if ((x - 1) < 20)
            {
                WriteAt(s, x, y);
                imprimir(s, x, y, sleep);
            }
            else
            {
                WriteAt(s, x - 1, y);
                imprimir(s, x - 1, y, sleep);
            }
        }

        else
        {
            if ((y + 1) > 25)
            {
                WriteAt(s, x, y);
                imprimir(s, x, y, sleep);
            }
            else
            {
                WriteAt(s, x, y + 1);
                imprimir(s, x, y + 1, sleep);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

小智 5

线程可能有一百万个微妙的问题 - 任何访问共享资源的东西都必须被认为是可疑的.

考虑一个移动位置跟随一个put字符不是原子的,一个线程可能会中断另一个线程导致move-move-put-put场景.实际上情况实际上比这更糟糕,因为控制序列本身受到发送到终端的多个字节的影响:因此控制序列本身可能变得腐败!

lock在终端访问周围使用关键区域防护().本lock应包含哪些必须的所有操作原子(不中断)就海誓山盟:

lock (foo) {
   move(...)
   draw(...)
}
Run Code Online (Sandbox Code Playgroud)

WriteAt适当地适应该功能.

但是,请记住,即使有这种变化,仍然存在微妙的竞争条件,请考虑:

  1. A被清除.
  2. 绘制A(到E的位置).
  3. E被清除(是刚刚绘制A的地方).
  4. E画出来了.

有了上述内容,有可能(在特定时间)E将出现在屏幕上而A不会出现.也就是说lock,在保护对控制台的访问权限的同时,自身无法充分保护线程和控制台之间的交互.

快乐的编码.


另请参见什么是常见的并发陷阱?一些一般的提示和链接.