C#中的多线程控制台游标操作竞争条件

时间:2013-06-18 01:32:22

标签: c# multithreading console-application race-condition

如果有人能给我一个指向正确方向的指针来解决我在过去两天里遇到的问题,我将非常感激:我正在为Visual Studio 2010控制台开发一个打字软件,需要表示时间并且能够同时从键盘输入。

问题是ReadLine()方法只允许我在计时器线程休眠时写入,也许我可以通过委托或高级任务延续方法来解决它,我有点困惑,因为这些是新概念。这是代码(抱歉丑陋的格式化):

using System;
using System.Threading.Tasks;
using System.Threading;


namespace Typing_Game_tests
{

    class Input_and_timer_multithread
    {
        public static void TimerThread() 
        { 
            for (int i = 1, mins = -1; i <= 1860; i++) 
            {
                Console.SetCursorPosition(0, 0);
                if (i % 60 == 1) 
                { 
                    mins++; 
                }

                Console.WriteLine("Timer: " + mins +  " minute(s) and " + i % 60 + " seconds elapsed");            

                Console.SetCursorPosition(0, 6); 
                Thread.Sleep(1000);
            }
        }


        static string keysRead;

        public static void GetInput()
        {
            while (Console.ReadKey(true).Key != ConsoleKey.Enter)
            {
                Console.SetCursorPosition(0, 6);
                keysRead = Console.ReadLine();
                Console.SetCursorPosition(0, 6);
            }

            Console.SetCursorPosition(0, 6);
            Console.WriteLine(keysRead);

        }

        static void Main(string[] args)
        {               
            Thread timerMain = new Thread(new ThreadStart(TimerThread));
            timerMain.Start();
            Task getinputMain = new Task(GetInput);
            getinputMain.Start();
        }
     }
  }

我发现和研究过的大多数例子都有lambda表达式和委托,这些是新的主题我仍然有一些难以理解并因此实现。 如果我能够在ReadLine()完成他的工作之前停止主计时器线程会更容易,但它没有意义;加上我尝试过使用Timers命名空间,但我遇到了同样的问题。

我想给人的印象是光标永久地从用户那里获得输入,但是我仍然没有找到一种方法来有效地来回切换线程而不会使它们死锁。 感谢。

1 个答案:

答案 0 :(得分:2)

自.NET 4.5以来,这种代码不再起作用了。 Console.ReadKey()和ReadLine()采用锁定来阻止其他线程写入控制台。你需要更换它,这样就不能再锁了。这需要使用Console.KeyAvailable 轮询键盘。这是Console.ReadLine()的简单替换方法:

static object ConsoleLock = new object();

static string ReadString() {
    var buf = new StringBuilder();
    for (; ; ) {
        while (!Console.KeyAvailable) System.Threading.Thread.Sleep(31);
        lock(ConsoleLock) {
            var key = Console.ReadKey(true);
            if (key.Key == ConsoleKey.Enter) {
                Console.WriteLine();
                return buf.ToString();
            }
            else if (key.Key == ConsoleKey.Backspace) {
                if (buf.Length > 0) {
                    buf.Remove(buf.Length - 1, 1);
                    Console.Write("\b \b");
                }
            }
            else {
                buf.Append(key.KeyChar);
                Console.Write(key.KeyChar);
            }
        }
    }
}

同时锁定移动光标的其他线程中的ConsoleLock,以便输出严格分开。