I'm trying to use a thread multiple 开发者_C百科times and have the thread stop processing if the user doesn't react fast enough.
Thread ask = new Thread (new ThreadStart (MathQuiz.prompt));
ask.Start();
ask.Join(30000);
if (answer == 4)
{
score = score+1;
answer = 0;
}
Console.WriteLine();
Console.WriteLine("Press any key to move on to the next question!");
Console.WriteLine();
Console.ReadKey(true);
Console.WriteLine("What is 15 / 3?");
Console.WriteLine();
ask.Start();
ask.Join(30000);
if (answer == 5)
{
score = score+1;
answer = 0;
}
...
static void prompt()
{
preanswer = (Console.ReadLine());
if (!decimal.TryParse(preanswer, out answer))
{
Console.WriteLine("That wasn't even a number or decimal!");
}
else
{
answer = decimal.Parse(preanswer);
}
}
Right now it doesn't seem that the "prompt" thread is terminating, and so it crashes when the second question begins.
So I need a solution! I of course don't mind to answer questions to help myself get an answer.
The method Thread.Join(Int32)
doesn't stop the other thread after the given number of milliseconds. It just stops waiting. It returns true if the other thread has terminated.
So if ask.Join(30000);
returns false, the other thread is still running and you have to Abort the thread yourself.
What evidence do you have that the prompt thread is not terminating? The main thread should be waiting for the prompt thread to terminate at the "join" places, so, if execution continues past a join, the "prompt" thread terminated.
Take a look at the example on MSDN's Thread.Join() page. The example uses two different methods for passing work to a thread. regularThread
is what you are trying to do. For the simple task in your example, I think doing a join() without events or locks is a reasonable solution. If you are prototyping to make a product that is much more robust than your example implies, then you should also: 1) read up on the ThreadPool . It avoids the cost of creating/disposing threads. 2) Put a lock() block around reads and writes to the answer
variable.
A word of warning: Other answers mention using Thread.Abort(). Calling Thread.Abort() on the currently executing thread is fine and nearly equivalent of throwing an exception. However calling Thread.Abort() on a different thread should be avoided. There's several scenarios where it can result in the thread not cleaning up properly.
Console.ReadLine
blocks the current thread from being aborted until the line has been read. (As per comment from Timwi)
To work around this, you would have to use the Console.KeyAvailable
method here: How to add a Timeout to Console.ReadLine()?
I re-wrote the problem before realising my mistake, and now here's a solution.
It's rather more involved than I wanted it to be. (Using the KeyAvailable means I need to queue up the keys entered, and to support backspace I have to remove the items. I also need to sleep while no keys are available... )
private static AutoResetEvent answered = new AutoResetEvent(false);
private static Func<string, bool> questionCorrect = null;
private static bool? correct;
static void Main(string[] args)
{
int score = 0;
AskQuestion(ref score,
"What is 15 / 3?",
TimeSpan.FromSeconds(5),
answer =>
{
decimal value;
if (!decimal.TryParse(answer, out value))
{
Console.WriteLine(
"That was not a valid number");
return false;
}
return (value == 15/3);
});
AskQuestion(ref score,
"What is 20 * 2 ?",
TimeSpan.FromSeconds(5),
answer =>
{
decimal value;
if (
!decimal.TryParse(answer,
out value))
{
Console.WriteLine(
"That was not a valid number");
return false;
}
return (value == 20*2);
});
Console.WriteLine("Done. Score: {0}", score);
Console.ReadLine();
}
private static void AskQuestion(ref int score, string question, TimeSpan duration, Func<string, bool> validator)
{
// Setup
questionCorrect = validator;
correct = null;
answered.Reset();
// Ask
Console.WriteLine(question);
Thread thread = new Thread(GetQuestion);
thread.Start();
// Wait
answered.WaitOne(duration);
thread.Abort();
thread.Join();
Console.WriteLine(); // Write empty line, otherwise this overwrites the answer.
// Validate);
if (correct.HasValue && correct.Value == true)
{
score++;
Console.WriteLine("Correct");
}
else if (correct.HasValue)
{
Console.WriteLine("Incorrect");
}
else
{
Console.WriteLine("Timeout");
}
}
private static void GetQuestion()
{
try
{
List<char> captured = new List<char>();
bool answerCaptured = false;
while (!answerCaptured)
{
while (Console.KeyAvailable)
{
var key = Console.ReadKey();
if (key.KeyChar == '\r' || key.KeyChar == '\n')
{
answerCaptured = true;
break;
}
if (key.KeyChar == '\b' && captured.Count > 0)
{
captured.RemoveAt(captured.Count - 1);
}
else
{
captured.Add(key.KeyChar);
}
}
Thread.Sleep(50);
}
string answer = new string(captured.ToArray());
correct = questionCorrect.Invoke(answer);
answered.Set();
}
catch (ThreadAbortException)
{
// will be thrown when the thread times out.
}
}
精彩评论