Threading: Variable Access and Termination of a Thread - c#

Let me start by showing you my code so far:
using System;
using System.Threading;
class MathQuiz
{
static void Main()
{
int score = 0;
string preanswer;
decimal answer = 0;
Console.WriteLine("Welcome to Project5, a MathQuiz project.");
Console.WriteLine("You will be asked 10 questions, and will have 30 seconds to read and answer each one.");
Console.WriteLine("Press any key to begin.");
Console.ReadKey(true);
Console.WriteLine("What is 2 + 2?");
Thread ask = new Thread (new ThreadStart (MathQuiz.prompt));
ask.Start();
Thread.Sleep(3000);
//This is where I want to end the thread if it isn't already done.
if (answer == 4)
{
score = score+1; //Here's where I don't know if my adding is correct.
}
Console.WriteLine("Press any key to move on to the next question!");
Console.ReadKey(true);
}
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);
}
}
}
So, when I try and compile this code, I get CS0103 errors for preanswer and answer in the "prompt" method.
This leads to 3 questions:
What EXACTLY do I have to do to make preanswer and answer accessible to the "prompt" method?
Did I add 1 onto the score variable correctly?
How can I terminate a thread if it is running? (In this case, the "ask" thread wouldn't end until they typed an answer.)
Please just tell me what to change. I don't know coding words and terminology because I just started a few weeks ago. Please try to be as clear as possible.

static string preanswer;
static decimal answer = 0;
static void Main()
{
int score = 0;
//string preanswer;
//decimal answer = 0;
...
etc.

To wait for the thread, use Join()... This will tell the thread which the function is called on to wait for the thread until it joins back:
ask.Join(int);

Related

How to put User Input to end the loop on c#

Im new to programming in C# and i encountered some problems, I am trying to create a countdown Console Command but im stuck on how to terminate the loop using a user input (in this case, when the user presses the "Enter" Button)
Here is the code that I currently have
using System;
using System.Threading;
class stopWatch {
public static void Main (string[] args) {
Console.WriteLine ("Access The Clock? Y/N");
string yN = Console.ReadLine();
if ((yN == "y") || (yN == "Y")) {
Console.WriteLine ("Timer (T) or Stopwatch (S)?");
var sT = Console.ReadLine();
if ((sT == "s") || (sT == "S")) {
Console.WriteLine ("Press the 'Enter' Button to Start");
Console.ReadLine();
Console.WriteLine("Stopwatch Started");
Console.WriteLine("Press the 'Enter' Button again to Stop");
for(int i = 0; i >= 0; i ++) {
Console.WriteLine(i);
Thread.Sleep(1000);
}
}
}
else if ((yN =="n") || (yN == "N")) {
Console.WriteLine ("Alright, Thank You");
Environment.Exit(0);
}
else {
Console.WriteLine ("Wrong Input");
Environment.Exit (0);
}
}
}
keep in mind im very new to c# and loops, i'm getting a hard time translating and trying on everything, it'd be really grateful of me to get an extra explanation for your answers
You need to use separate thread for that. Threads is advanced theme, but the basic goal is to do several things at once (in your example run stopwatch and wait for user input). Here is full working code based on your sample:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp13
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Access The Clock? Y/N");
string yN = Console.ReadLine().ToLower();
if (yN == "y")
{
Console.WriteLine("Timer (T) or Stopwatch (S)?");
var sT = Console.ReadLine().ToLower();
if (sT == "s")
{
Console.WriteLine("Press the 'Enter' Button to Start");
Console.ReadLine();
bool runStopWatch = true;
Task.Run(() =>
{
for (int i = 0;; i++)
{
Console.WriteLine(i);
Thread.Sleep(1000);
if (!runStopWatch)
{
break;
}
}
});
Console.WriteLine("Stopwatch Started");
Console.WriteLine("Press the 'Enter' Button again to Stop");
Console.ReadLine();
runStopWatch = false;
}
}
else if (yN == "n")
{
Console.WriteLine("Alright, Thank You");
Environment.Exit(0);
}
else
{
Console.WriteLine("Wrong Input");
Environment.Exit(0);
}
}
}
}
Task.Run starts a new thread, so your stopwatch runs independent of the main thread (which waits for user to press Enter). Variable runStopWatch is shared between threads (don't think about it right now), so when main thread sets it to false, stopwatch's thread sees it and terminated the loop.
Side mote. More proper way to do this is to use CancellationToken istead of bool variable. But again. don't worry about it right now.
Since you're new im going to try to explain it as simple as possible. The solution you're trying to create is a solution to a multi-threaded problem. This means you will have to do an action while another action takes place. This can be resolved using async methods, this might be too advanced for now, so I will suggest reading up on loops and then multi-threaded programming first.
But for an answer i will have to refer to this post here on how to fix this particular problem. (Look at the second part of the accepted answer).

Why doesn't void Main start after the if-condition?

Void Main won't start again after the if-statement.
I have been experimenting for a while with such constructions and normally they work. But that's not the case. The generator void is somewhere below, please don't take it into consideration.
class Program
{
private static string Input = Console.ReadLine();
static void Main()
{
Console.WriteLine("Press any key to start");
Program.Generator();
Console.WriteLine("Are you satisfied? Type '1' if yes or '2' if no.");
Console.ReadLine();
if (Input == "2")
Program.Main();
}
}
No errors, no mistakes, the console just closes after completing Main.
Welcome to stack overflow. You're not crazy, just missing a piece of information about programming and flow of control. Specifically, iteration statements.
The program you've written will exit any time Input holds a value other than 2 because there's no loop forcing it to run over and over again.
static void Main()
{
Console.WriteLine("Press any key to start");
Console.ReadKey();
do
{
// Program.Generator();
Console.WriteLine("Are you satisfied? Type '1' if yes or '2' if no.");
} while (Console.ReadKey().KeyChar != '1');
}

Running Code After Infinite While Loop

I'm trying to make a console application. I want the ". . ." after "Press any key to exit" to blink.
The blinking is working fine and all, but now my Console.ReadKey() is unreachable code.
I'm just wondering if there is anyway to let the Console.ReadKey() line be reached, or where I would move it to be able to let it be run?
static void Main(string[] args)
{
string blinkExit = ". . ."; //Variable for blinking periods after 'exit'
Console.Write("\n\nPress any key to exit"); //Displays a message to press any key to exit in the console
while (true)
{
WriteBlinkingText(blinkExit, 500, true);
WriteBlinkingText(blinkExit, 500, false);
}
Console.ReadKey();
}
private static void WriteBlinkingText(string text, int delay, bool visible)
{
if (visible)
Console.Write(text);
else
for (int i = 0; i < text.Length; i++)
Console.Write(" ");
Console.CursorLeft -= text.Length;
System.Threading.Thread.Sleep(delay);
}
It seems like you just want this text to blink until a key is pressed by the user. If that is the case then why not just loop until there is a key available to be read?
while (!Console.KeyAvailable)
{
WriteBlinkingText(blinkExit, 500, true);
WriteBlinkingText(blinkExit, 500, false);
}
This avoids all of the nasty issues that can come up with using multiple threads
just change your while code with this
Task.Factory.StartNew(() =>
{
while (true)
{
WriteBlinkingText(blinkExit, 500, true);
WriteBlinkingText(blinkExit, 500, false);
}
});
pressing any key will close the console
Imagine threads as workers, when you use the console application, you are working on one thread, and by using a loop which doesn't end, you are essentially stopping any other task from occurring from that point onwards because the thread is busy looping!
To solve the problem you need to place a trigger of sorts in the loop which is going to exit once a key is pressed. I've demonstrated a simple way to do this below.
Examine the code, you'll find that I've used a new class called Interlocked, the reason I used this is to avoid any thread safety issues, thread safety is a deep topic, and isn't something I want to be trying to explain in a few sentences, but the idea is that any resources being shared between two different threads, must be locked. You'll also find a lambda expression, these are all intermediate topics, and trying to learn them now is going to achieve one thing... a headache!
private static long _isKeyPressed;
private static void Main()
{
// Create a new Thread and Start it.
new Thread(() =>
{
// Variable for blinking periods after 'exit'
const string blinkExit = ". . .";
// Displays a message to press any key to exit in the console
Console.Write("\n\nPress any key to exit");
// Run until no keys are pressed.
while (Interlocked.Read(ref _isKeyPressed) == 0)
{
WriteBlinkingText(blinkExit, 500, true);
WriteBlinkingText(blinkExit, 500, false);
}
}).Start();
Console.ReadKey();
// Once a key has been pressed, increment the value of _isKeyPressed to 1.
// This will indicate to the thread running above that it should exit it's loop.
// as _isKeyPressed is no longer equal to 0.
Interlocked.Increment(ref _isKeyPressed);
}
private static void WriteBlinkingText(string text, int delay, bool visible)
{
if (visible) Console.Write(text);
else
{
for (int i = 0; i < text.Length; i++)
{
Console.Write(" ");
}
}
Console.CursorLeft -= text.Length;
Thread.Sleep(delay);
}

Passing Variable through function to main()

I'm very new to C# but am learning more and more every day. Today, I'm trying to build a simple console calculator and need help passing a variable from the function to Main() so I can use it in an if-else to determine what function should execute.
public static void Main(string[] args)
{
int decision = Introduction();
Console.Clear();
Console.WriteLine(decision);
Console.ReadLine();
}
public static int Introduction()
{
int decision = 0;
while (decision < 1 || decision > 7)
{
Console.Clear();
Console.WriteLine("Advanced Math Calculations 1.0");
Console.WriteLine("==========================");
Console.WriteLine("What function would you like to perform?");
Console.WriteLine("Press 1 for Addition ++++");
Console.WriteLine("Press 2 for Subtraction -----");
Console.WriteLine("Press 3 for Multiplication ****");
Console.WriteLine("Press 4 for Division ////");
Console.WriteLine("Press 5 for calculating the Perimeter of a rectangle (x/y)");
Console.WriteLine("Press 6 for calculating the Volume of an object (x/y/z)");
Console.WriteLine("Press 7 for calculating the standard deviation of a set of 10 numbers");
decision = int.Parse(Console.ReadLine());
if (decision < 1 || decision > 7)
{
decision = 0;
Console.WriteLine("Please select a function from the list. Press Enter to reselect.");
Console.ReadLine();
}
else
{
break;
}
}
return decision;
}
When I try to use decision up in Main() it says "The name decision does not exist in the current context".
I'm stumped and tried googling it to no avail.
Cheers
SUCCESS!
Return the value from Introduction. The value is local to the method and to use it elsewhere you need to return it and assign to a local variable. Alternatively, you could make decision a static class variable, but that's not a particularly good practice, at least in this case. The Introduction method (not a particularly good name, IMO, it should probably be GetCalculationType() since that is what it is doing) typically shouldn't have any side-effects.
public static void Main( string[] args )
{
int decision = Introduction();
...
}
public static int Introduction()
{
int decision = 0;
...
return decision;
}
Main() is the entry point for your app. It then calls your method Introduction() which adds a new stack frame on the stack. Because you declare the decision variable inside your Introduction method, the Main method has no knowledge of it.
If you instead declare your decision variable outside both methods, you should be able to reference it from either:
int decision;
static void Main(string[] args)
{
// code here
}
static void Introduction()
{
// code here
}
You can't use the decision variable in main since it is local to the function Introduction.
You could make decision a static class variable but better would be to return the value from Introduction and assign it to a local variable in main.

How to stop a running method with keyboard input in a Console Application on C#?

In short, I'm utilizing C# to scientific computation and I've written a method that has a while loop that may run to a user-specified quantity of steps... Actually, this method may take too long to execute (like more than 5 hours). When it takes this long, I may want to stop the method pressing Esc key, for example.
As I read something about breaking while, it is as simple as a Boolean flag or something like this. So I thought in something like this:
public Double? Run(int n)
{
int i = 0;
while ((i < n) && (/* inputkey != ConsoleKey.Escape */))
{
// here goes the heavy computation thing
// and I need to read some "inputkey" as well to break this loop
i++;
}
// I'm not worried about the return statement, as it is easy to do...
// returns null if the user skipped the method by pressing Escape
// returns null if the method didn't converged
// returns the double value that the method calculated otherwise
}
Well, this is what I wondered until now... So please, could you give useful ideas to this extent? How can I wait for a user input (I thought about Events, but I'm not sure how to implement it here and I think that it will make the code even slower, if I have to listen to a key at every while step the code goes into...
Well, any ideas or comments?
Update: I think I should have had described better the problem. All the solutions you gave me may solve this problem I proposed, but I think I was not completely reliable to my real problem. I don't know if I should ask another question or keep with this one...
You could run this method from a separate thread and set a stop variable when a key is pressed:
object myLock = new object();
bool stopProcessing = false;
public Double? Run(int n)
{
int i = 0;
while (i < n)
{
lock(myLock)
{
if(stopProcessing)
break;
}
// here goes the heavy computation thing
// and I need to read some "inputkey" as well to break this loop
i++;
}
}
and when a key is pressed, update stopProcessing accordingly:
Console.ReadKey();
lock(myLock)
{
stopProcessing = true;
}
If you're just wanting to stop the application, Ctrl-C from the command line will do it. If you really need to intercept input during a long running process, you might want to spawn a worker thread to do the long running process and then just use the main thread to interact with the console (i.e. Console.ReadLine()).
You will need to do this using threading. When you start the task, spawn a new thread and execute the task on that thread. Then in your Program.cs, wait for user input. If the user enters something meaningful - in your case, the Esc key - alert the background thread of the action. The simplest way to do this is by setting a static variable. The background thread will be checking this static variable and when it has been changed, the background thread will clean itself up and abort.
See the MSDN article on Threading.
A code sample will be a little more in depth, but it would look something like this:
public class Program.cs
{
public static myFlag = false;
public void Main()
{
thread = new Thread(new ThreadStart(DoWork));
thread.Start();
Console.ReadLine();
myFlag = true;
}
public static DoWork()
{
while(myFlag == false)
{
DoMoreWork();
}
CleanUp()
}
public static DoMoreWork() { }
public static CleanUp() { }
}
pool on Console.KeyAvailable in timely manner and take the action accordingly.
using System;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
class Program
{
static bool _cancelled = false;
static void Main( string[] args )
{
var computationTask = Task.Factory.StartNew(PerformIncredibleComputation);
var acceptCancelKey = Task.Factory.StartNew(AcceptCancel);
while (!acceptCancelKey.IsCompleted && ! computationTask.IsCompleted)
{
computationTask.Wait (100);
}
if( acceptCancelKey.IsCompleted && !computationTask.IsCompleted )
{
computationTask.Wait (new System.Threading.CancellationToken ());
}
else if(!acceptCancelKey.IsCompleted)
{
acceptCancelKey.Wait(new System.Threading.CancellationToken());
}
}
private static void PerformIncredibleComputation()
{
Console.WriteLine("Performing computation.");
int ticks = Environment.TickCount;
int diff = Environment.TickCount - ticks;
while (!_cancelled && diff < 10000)
{
//computing
}
Console.WriteLine("Computation finished");
}
private static void AcceptCancel()
{
var key = Console.ReadKey(true);
Console.WriteLine("Press Esc to cancel");
while(key.Key != ConsoleKey.Escape)
{
key = Console.ReadKey(true);
}
_cancelled = true;
Console.Write("Computation was cancelled");
}
}
}

Categories