线程未按预期启动

3

我正在尝试制作一个测试,以查看某人是否具备特定的技能。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class timerup
    {
        public bool timeup = false;
    }

    class Program
    {
        public static void timer()
        {
            for (int i = 1; i < 3; i++)
            {
                System.Threading.Thread.Sleep(1000);
                if (i == 5)
                {
                    object a;
                    a = true;
                    a = new timerup();
                    timerup ClassRef;
                    ClassRef = (timerup)a;
                    ClassRef.timeup = true;
                }
            }
        }

        static void Main(string[] args)
        {
            Console.Title = "The Secret Agent Test";
            Console.BackgroundColor = ConsoleColor.Black;
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("Welcome, agent. This is the test to see if\nyou are good enough to be a full member of the OT Secret Agency.");
            Console.WriteLine("Do you want to continue? [Y/N]");
            string cont = Console.ReadLine();
            if (cont == "y" || cont =="Y")
            {
                Console.Clear();
                Console.WriteLine("Let's continue the test.");
                Console.WriteLine("Crack the password:");
                Console.WriteLine("Username: IDIOT_NOOB1337\nPROFILE: Likes memes such as doge.\nIs an elitist (Over the things he likes)\nOnly uses the word idiot as an insult");
                Console.WriteLine("Password:");
                string pass1 = Console.ReadLine();
                if (pass1 == "AnyoneWhoDoesn'tLikeDogeIsAnIdiot" || pass1 == "anyonewhodoesn'tlikedogeisanidiot")
                {
                    Console.WriteLine("Account accessed.");
                    Console.WriteLine("Stage 1 Complete.");
                    Console.WriteLine("Loading next level...");
                    System.Threading.Thread.Sleep(2000);
                    Console.WriteLine("Level 2 loaded.");
                    System.Threading.Thread.Sleep(1000);
                    Console.Clear();
                    Console.WriteLine("Nice. You certainly have skill. But this test.... determines speed of mind.");
                    System.Threading.Thread.Sleep(2500);
                    Console.Clear();
                    Console.WriteLine("You only have two seconds to answer the next question. Press any key when ready.");
                    Console.ReadKey();
                    Console.Clear();
                    Console.WriteLine("What is 12x12?!"); // QUESTION
                    System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(timer)); // SUCH COMPLEX CODE FOR A TIMER... WTF.
                    string product = Console.ReadLine();
                    object b;
                    b = true;
                    b = new timerup();
                    timerup ClassRef;
                    ClassRef = (timerup)b;
                    bool timerthing = ClassRef.timeup;
                    if (product != "144" || timerthing == true)
                    {
                        Console.WriteLine("Sorry, you are incorrect. Restart the test again.");
                        System.Threading.Thread.Sleep(2000);
                        Console.Clear();
                        System.Environment.Exit(-1);
                    }
                    else
                    {
                        Console.WriteLine("Impressive. Your mind is fast, too. Well, be prepared for the next test. Pressure.");
                    }
                }
            }
        }
    }
}

线程没有执行;我怀疑是因为 string product = Console.ReadLine(); 这一部分。这个测验的第二个问题是12x12,你有2秒钟来回答,但是计数两秒的线程没有被执行...为什么呢?如果你知道原因,应该如何修复?


2
你已经创建了一个线程t,但是没有调用它的代码,比如t.Start()。在ReadLine()之前加上t.Start()。 - Yohanes Nurcahyo
2
使用Stopwatch更好,将t.Start()翻译为中文。 - jaket
这里有相当多的代码是没有用的(对象引用和类型转换),而且它可能不会做你期望的事情。例如,"timerthing"总是为false —— timer 函数将 timeup 设置为true,但它操作的是不同的 timerup 实例,因此它不会影响if-else语句。 - Pieter Witvoet
不要使用 Environment.Exit。只需将您的 Main 方法的返回类型更改为 intreturn -1;。另外,当用户失败游戏时,请不要返回 -1,因为这会被解释为应用程序中的错误,并且某些启动器(如Total Commander)可能会告诉您启动应用程序时出现错误。只需保持返回类型为 void 并执行简单的 return; 即可。 - Luaan
仔细看 for (int i = 1; i < 3; i++) { ... if (i == 5) ... } - H H
1
请不要在问题标题中加入“已解决”;这并不会在用户界面中标记问题为已回答。如果其中一个答案解决了您的问题,请单击其左侧的绿色复选标记;这将标记您的问题为已回答。如果您自己解决了问题,请在下方输入您自己的答案,并在几天后单击其绿色复选标记。 - Dour High Arch
4个回答

6
你只创建了一个线程,你也应该启动它。
System.Threading.Thread t = new System.Threading.Thread(timer);
t.Start();

5

我写了一个示例,演示如何在不使用线程的情况下检查经过的时间。

        bool isInTime = false;

        var start = DateTime.Now;
        Console.WriteLine("answer this in 5 seconds, what is 2x2");
        var answer = Console.ReadLine();

        if ((DateTime.Now - start).TotalSeconds <= 5)
            isInTime = true;

        if (isInTime && answer == "4")
            Console.WriteLine("Good job you are now an agent");
        else
            Console.WriteLine("To slow and too dumb");

        Console.ReadKey();

秒表是另一个选择:http://www.dotnetperls.com/stopwatch 如果您真的需要线程(这对于此问题来说有点过度),可以在此处找到一些很好的示例:https://msdn.microsoft.com/en-us/library/ts553s52(v=vs.110).aspx

2
两个回答都很到位,让我补充一下如何创建不那么复杂的计时器 :)
var timeIsUp = false;
var timer = new Timer(_ => { timeIsUp = true; }, null, 5000, Timeout.Infinite);

总的来说,@JensB是绝对正确的——使用多线程应该是最后的选择。正确地处理多线程非常困难,因此避免使用它是一个相当不错的策略。我展示的Timer示例也是多线程的——定时器上的回调将在不同的线程上发生。这会引入同步问题,但对于像这样简单的情况,它们不应该太痛苦。要改进这一点,您至少需要确保本地变量被安全地更新:
var syncObject = new object(); 
var timeIsUp = false;
var timer = new Timer(_ => { lock (syncObject) { timeIsUp = true; } }, null, 5000, 
                      Timeout.Infinite);

var answer = Console.ReadLine();

lock (syncObject)
{
  if (timeIsUp) ...
}

现在手动使用Thread已经完全没有必要了。使用Task来进行并发和多线程更加容易。例如:

var timerTask = Task.Delay(5000);

var answer = Console.ReadLine();

if (timerTask.IsCompleted) Console.WriteLine("Too late");

在我看来,最好的选择是使用适当的异步API - 不幸的是,.NET Console类没有这些API。尽管它看起来很傻,但似乎这是一个相当不错的选择:

void Main()
{
    var cts = new CancellationTokenSource();
    cts.CancelAfter(TimeSpan.FromSeconds(2));

    var task = Task.Run(() => ReadLineFromConsole(cts.Token));
    task.Wait(cts.Token);

    if (task.IsCanceled)
    {
        Console.WriteLine("Too slow!");
        return;
    }

    var result = task.Result;

    if (result != "144")
    {
        Console.WriteLine("Wrong!");
        return;
    }

    // Continue
}

public string ReadLineFromConsole(CancellationToken token)
{  
    var buffer = new StringBuilder();
    int ch;

    while (!token.IsCancellationRequested)
    {
        Console.In.Peek();

        token.ThrowIfCancellationRequested();

        ch = Console.In.Read();
        if (ch == -1) return buffer.Length > 0 ? buffer.ToString() : null;

        if (ch == '\r' || ch == '\n') 
        {
            if (ch == '\r' && Console.In.Peek() == '\n') Console.In.Read();
            return buffer.ToString();
        }

        buffer.Append((char)ch);
    }

    token.ThrowIfCancellationRequested();

    // Shouldn't be reached, but the compiler doesn't know that.
    return null;
}

这种方法的有趣之处在于,即使用户没有按下回车键,我也可以退出应用程序(并中止输入)。它还允许您使用await将复杂的工作流程绑定在一起,尽管在控制台应用程序中稍微有些棘手。
辅助方法ReadLineFromConsole实际上与通常的ReadLine方法相同,但它还检查取消操作,并防止它从后续的ReadLine调用中“窃取”数据,因此它将首先执行Peek。这并不使它线程安全 - 您仍然不应该同时使用多个线程从不同的线程读取多行。但这意味着当最终输出出现时,我们可以忽略它。请记住,线程将一直等待,直到控制台输入出现 - 不要使用它来启动多个同时请求,而没有确保最终会有一些输入(例如,在ReadLineFromConsole调用之间使用通常的Console.ReadLine等)。

0

对你的代码进行一些重构并解决你的问题:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Diagnostics;

namespace ConsoleApplication2
{
    class Program
    {
        static void WriteText(params string[] lines) { WriteText(0, lines); }

        static void WriteText(double delaySecs, params string[] lines)
        {

            for (int i = 0; i < lines.Length; i++) Console.WriteLine(lines[i]);
            if (delaySecs > 0) Thread.Sleep(TimeSpan.FromSeconds(delaySecs));
        }

        static void Main(string[] args)
        {
            Console.Title = "The Secret Agent Test";
            Console.ForegroundColor = ConsoleColor.Green;
            WriteText("Welcome, agent. This is the test to see if\nyou are good enough to be a full member of the OT Secret Agency.", "Do you want to continue? [Y/N]");
            var readk = Console.ReadKey();
            if (readk.Key == ConsoleKey.Y || readk.Key == ConsoleKey.N)
            {
                Console.Clear();
                WriteText("Let's continue the test.\n", "Crack the password:\n", "Username: IDIOT_NOOB1337\nPROFILE: Likes memes such as doge.",
                 "Is an elitist (Over the things he likes)", "Only uses the word idiot as an insult", "Password:");
                string pass1 = Console.ReadLine();

                if (pass1 != "AnyoneWhoDoesn'tLikeDogeIsAnIdiot" && pass1 != "anyonewhodoesn'tlikedogeisanidiot") return;

                WriteText(2, "Account accessed.", "Stage 1 Complete.", "Loading next level...");                
                WriteText(1, "Level 2 loaded.");                
                Console.Clear();
                WriteText(2.5, "Nice. You certainly have skill. But this test.... determines speed of mind.");                
                Console.Clear();
                Console.WriteLine("You only have two seconds to answer the next question. Press any key when ready.");
                Console.ReadKey();
                Console.Clear();
                Console.WriteLine("What is 12x12?!"); // QUESTION

                int allowedTime = 2 * 1000; // time allowed
                new Thread(() =>
                {
                    Stopwatch s = new Stopwatch();
                    s.Start();
                    while (s.ElapsedMilliseconds < allowedTime) { }
                    WriteText(2, "Sorry, you're too late. Restart the test again.");                    
                    Console.Clear();
                    Environment.Exit(-1);
                }).Start();

                string product = Console.ReadLine();
                if (product == "144") Console.WriteLine("Impressive. Your mind is fast, too. Well, be prepared for the next test. Pressure.");

                WriteText(2, "Sorry, you are incorrect. Restart the test again.");                
                Console.Clear();                
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接