繁体   English   中英

C#多线程控制台动画

[英]C# Multi-Threaded Console Animation

我一直在做一个控制台游戏,在其中制作动画。 通常,这可以在控制台中轻松完成,但是我每个人都有一个单独的线程,因此游戏可以在动画过程中继续进行。

static void Main(string[] args)
    {
        animateLine(wave, 50, 10, Console.CursorLeft, Console.CursorTop);
        Console.Read();
    }

    static void animateLine(string[] an, int i, int sec, int x, int y)
    {
        new Thread(delegate ()
        {
            Console.Write("  ");
            Console.Write("\b");
            sec = sec * 1000;
            var time = Stopwatch.StartNew();
            while (time.ElapsedMilliseconds < sec)
            {
                Console.SetCursorPosition(x, y);
                foreach (string val in an)
                {
                    Console.SetCursorPosition(x, y);
                    Console.Write(val);
                    Thread.Sleep(i);
                }
            }
            Console.SetCursorPosition(x, y);
            foreach (char cal in an.GetValue(0).ToString())
            {
                Console.Write(" ");
            }
            foreach (char cal in an.GetValue(0).ToString())
            {
                Console.Write("\b");
            }
            Console.Write(" \b");
            time.Stop();
            time.Reset();
        }).Start();
    }

上面创建了一个animateLine实例,该实例将wave循环10秒钟。 即使创建两个实例或总共三个线程,这也可以正常工作。

static void Main(string[] args)
    {
        animateLine(wave, 50, 10, Console.CursorLeft, Console.CursorTop);
        Console.Write(" \n");
        animateLine(wave, 50, 10, Console.CursorLeft, Console.CursorTop);
        Console.Read();
    }

但是,当其他线程数超过2时,它开始中断。 在某些情况下,动画的一帧多余部分会留在后面,该wave通常仅包含11个字符,卡在一帧中的附加wave会出现在它的旁边,并在动画完成后保留。 我尝试了其他方式运行线程,但是它们只是以其他方式破坏了动画,这是我发现的唯一能够产生平滑动画的线程。

评论员罗恩·拜尔的观察是正确的。 您的线程正在争夺控制台输出的控制权,并且在写入单个一致的输出单元之前,每个线程都可以被另一个线程抢占。

解决此问题的方法是标准的多线程101:在需要作为原子单元运行而不会中断的代码段周围放置一个锁。 这是您的代码版本,显示我的意思:

class Program
{
    static void Main(string[] args)
    {
        string[] wave = { "-", "/", "|", "\\" };
        animateLine(wave, 50, 10, Console.CursorLeft, Console.CursorTop);
        lock (_lock)
        {
            Console.Write(" \n");
        }
        animateLine(wave, 50, 10, Console.CursorLeft, Console.CursorTop);
        Console.Read();
    }

    private static readonly object _lock = new object();

    static void animateLine(string[] an, int i, int sec, int x, int y)
    {
        Thread thread = new Thread(delegate()
        {
            Console.Write("  ");
            Console.Write("\b");
            sec = sec * 1000;
            var time = Stopwatch.StartNew();
            while (time.ElapsedMilliseconds < sec)
            {
                foreach (string val in an)
                {
                    lock (_lock)
                    {
                        Console.SetCursorPosition(x, y);
                        Console.Write(val);
                    }
                    Thread.Sleep(i);
                }
            }

            lock(_lock)
            {
                Console.SetCursorPosition(x, y);
                foreach (char cal in an.GetValue(0).ToString())
                {
                    Console.Write(" ");
                }
                foreach (char cal in an.GetValue(0).ToString())
                {
                    Console.Write("\b");
                }
                Console.Write(" \b");
            }
            time.Stop();
            time.Reset();
        });

        thread.IsBackground = true;
        thread.Start();
    }
}

最明显的两个部分在animateLine()方法中,在将一些文本写入屏幕之前,将调用SetCursorPosition() 至关重要的是,在编写此文本时,任何内容都不能打扰光标位置,否则会损坏输出。 在代码的这些部分周围lock可确保在发送任何其他输出之前,已完成给定起始光标位置的所有输出。

也许稍微不太明显的是Main()方法中的Console.WriteLine() 但这是出于相同的原因:虽然这部分代码未设置光标位置,但它绝对有可能影响光标位置,因此您还希望将其操作限制在其他关键部分被占用的时间之外执行。

请注意,在所有三个位置都需要这些锁。 lock语句用于确保互斥执行代码的受保护部分。 仅在其中一个节上放置锁不会阻止其他任何节在该节执行时执行。 运行时将无法知道哪些代码段需要互斥; 程序员可以在适当的地方完全使用lock语句来表明这一点。

还要注意,互斥是基于提供给lock语句的对象的。 对于任何给定的代码锁定段,该代码段仅与与同一对象锁定的任何其他关键段互斥。 因此,在更复杂的场景中,您可以通过将不同的锁定对象与需要互斥执行的不同代码组相关联,来提供更细粒度的锁定(即避免不相关的关键部分在彼此之间互斥)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM