Here I have simple code written in C#.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Reactive.Subjects;
namespace ReactiveProgramming
{
class Program
{
static void Main(string[] args)
{
var generateSeq = new GenerateSequence();
Console.WriteLine("Hello World!");
generateSeq.Sequence.Subscribe(val =>
{
Console.WriteLine(val);
// it works if I remove below two lines ...
Console.SetCursorPosition(0, Console.CursorTop - 1);
Console.Write("\r" + new string(' ', Console.WindowWidth) + "\r");
});
generateSeq.Run();
}
}
class GenerateSequence
{
public Subject<int> Sequence = new Subject<int>();
public void Run(int runTimes = 10)
{
ConsoleKeyInfo cki;
Task.Run(() => runForTimes(10));
do
{
cki = Console.ReadKey();
} while (cki.Key != ConsoleKey.Escape);
}
public void runForTimes(int runTimes = 10)
{
for (var i = 0; i < 10; i++)
{
Sequence.OnNext(i);
Thread.Sleep(1000);
}
}
}
}
But instead of printing sequence on top of each other, it just freeze the output after first emit.
And tested in Linux too ... same output.
If I remote these lines Console.SetCursorPosition and Console.Write("\r" + new string(' ', Console.WindowWidth) + "\r") from subscribe ... it works and print all numbers on screen one after another but I want to print on top of each other ...
But if I change my Main function like this:
static void Main(string[] args)
{
var generateSeq = new GenerateSequence();
Console.WriteLine("Hello World!");
generateSeq.Sequence.Subscribe(val =>
{
Console.WriteLine(val);
// Console.SetCursorPosition(0, Console.CursorTop - 1);
// Console.Write("\r" + new string(' ', Console.WindowWidth) + "\r");
});
generateSeq.Run();
}
Where I have commented those two lines ... output is as follows ...
But instead of output in sequence like second image, I want to print the output at the same position. Just over write the new output over the old one
Note: I am running it on Macbook Pro (Big Sur), it happens with .net core 3.1 or .net 5.0 and using iTerm as console emulator
If I were writing this, I'd go with this implementation:
static async Task Main(string[] args)
{
Console.WriteLine("Hello World!");
IObservable<System.ConsoleKeyInfo> keys =
Observable
.Start(() => Console.ReadKey());
await
Observable
.Interval(TimeSpan.FromSeconds(1.0))
.Take(10)
.TakeUntil(keys)
.Do(x =>
{
Console.WriteLine(x);
Console.SetCursorPosition(0, Console.CursorTop - 1);
},
() => Console.SetCursorPosition(0, Console.CursorTop + 1));
Console.WriteLine("Bye World!");
}
Wherever possible you should avoid using subjects.
SetCursorPosition works perfectly fine when not called from another thread. you can use an asynchronous approach to solve the problem instead of using Task.Run
class Program
{
static void Main(string[] args)
{
var generateSeq = new GenerateSequence();
Console.WriteLine("Hello World!");
generateSeq.Sequence.Subscribe(val =>
{
Console.WriteLine(val);
// move cursor back to previous line
Console.SetCursorPosition(0 ,Console.CursorTop - 1);
});
// start background operation
generateSeq.Run();
}
}
class GenerateSequence
{
public readonly Subject<int> Sequence = new();
public void Run(int runTimes = 10)
{
ConsoleKeyInfo cki;
// create a cancelation token, because if the user presses
// Escape key we don't need to run our background task
// anymore and the task should be stopped.
var tokenSource = new CancellationTokenSource();
var token = tokenSource.Token;
// we can not use await keyword here because we need to
// listen to ReadKey functions in case the user wants to
// stop the execution. without the await, task will run in
// the background asynchronously
var task = RunForTimes(runTimes,token);
// wait for the Escape key to cancel the execution or stop it
// if it's already running
do
{
cki = Console.ReadKey();
} while (cki.Key != ConsoleKey.Escape && !task.IsCompleted);
// cancel the background task if it's not compeleted.
if (!task.IsCompleted)
tokenSource.Cancel();
// Revert CursorPosition to the original state
Console.SetCursorPosition(0, Console.CursorTop + 1);
Console.WriteLine("Execution ends");
}
// we use an async task instead of a void to run our background
// job Asynchronously.
// the main difference is, we should not use a separate thread
// because we need to be on the main thread to safely access the Console (to read or write)
private async Task RunForTimes(int runTimes, CancellationToken token)
{
for (var i = 0; i < runTimes; i++)
{
Sequence.OnNext(i);
await Task.Delay(1000, token);
// exit the operation if it is requested
if (token.IsCancellationRequested) return;
}
}
}
Related
How can I continue to run my console application until a key press (like Esc is pressed?)
I'm assuming its wrapped around a while loop. I don't like ReadKey as it blocks operation and asks for a key, rather than just continue and listen for the key press.
How can this be done?
Use Console.KeyAvailable so that you only call ReadKey when you know it won't block:
Console.WriteLine("Press ESC to stop");
do {
while (! Console.KeyAvailable) {
// Do something
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
You can change your approach slightly - use Console.ReadKey() to stop your app, but do your work in a background thread:
static void Main(string[] args)
{
var myWorker = new MyWorker();
myWorker.DoStuff();
Console.WriteLine("Press any key to stop...");
Console.ReadKey();
}
In the myWorker.DoStuff() function you would then invoke another function on a background thread (using Action<>() or Func<>() is an easy way to do it), then immediately return.
The shortest way:
Console.WriteLine("Press ESC to stop");
while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
{
// do something
}
Console.ReadKey() is a blocking function, it stops the execution of the program and waits for a key press, but thanks to checking Console.KeyAvailable first, the while loop is not blocked, but running until the Esc is pressed.
From the video curse Building .NET Console Applications in C# by Jason Roberts at http://www.pluralsight.com
We could do following to have multiple running process
static void Main(string[] args)
{
Console.CancelKeyPress += (sender, e) =>
{
Console.WriteLine("Exiting...");
Environment.Exit(0);
};
Console.WriteLine("Press ESC to Exit");
var taskKeys = new Task(ReadKeys);
var taskProcessFiles = new Task(ProcessFiles);
taskKeys.Start();
taskProcessFiles.Start();
var tasks = new[] { taskKeys };
Task.WaitAll(tasks);
}
private static void ProcessFiles()
{
var files = Enumerable.Range(1, 100).Select(n => "File" + n + ".txt");
var taskBusy = new Task(BusyIndicator);
taskBusy.Start();
foreach (var file in files)
{
Thread.Sleep(1000);
Console.WriteLine("Procesing file {0}", file);
}
}
private static void BusyIndicator()
{
var busy = new ConsoleBusyIndicator();
busy.UpdateProgress();
}
private static void ReadKeys()
{
ConsoleKeyInfo key = new ConsoleKeyInfo();
while (!Console.KeyAvailable && key.Key != ConsoleKey.Escape)
{
key = Console.ReadKey(true);
switch (key.Key)
{
case ConsoleKey.UpArrow:
Console.WriteLine("UpArrow was pressed");
break;
case ConsoleKey.DownArrow:
Console.WriteLine("DownArrow was pressed");
break;
case ConsoleKey.RightArrow:
Console.WriteLine("RightArrow was pressed");
break;
case ConsoleKey.LeftArrow:
Console.WriteLine("LeftArrow was pressed");
break;
case ConsoleKey.Escape:
break;
default:
if (Console.CapsLock && Console.NumberLock)
{
Console.WriteLine(key.KeyChar);
}
break;
}
}
}
}
internal class ConsoleBusyIndicator
{
int _currentBusySymbol;
public char[] BusySymbols { get; set; }
public ConsoleBusyIndicator()
{
BusySymbols = new[] { '|', '/', '-', '\\' };
}
public void UpdateProgress()
{
while (true)
{
Thread.Sleep(100);
var originalX = Console.CursorLeft;
var originalY = Console.CursorTop;
Console.Write(BusySymbols[_currentBusySymbol]);
_currentBusySymbol++;
if (_currentBusySymbol == BusySymbols.Length)
{
_currentBusySymbol = 0;
}
Console.SetCursorPosition(originalX, originalY);
}
}
Here is an approach for you to do something on a different thread and start listening to the key pressed in a different thread. And the Console will stop its processing when your actual process ends or the user terminates the process by pressing Esc key.
class SplitAnalyser
{
public static bool stopProcessor = false;
public static bool Terminate = false;
static void Main(string[] args)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Split Analyser starts");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Press Esc to quit.....");
Thread MainThread = new Thread(new ThreadStart(startProcess));
Thread ConsoleKeyListener = new Thread(new ThreadStart(ListerKeyBoardEvent));
MainThread.Name = "Processor";
ConsoleKeyListener.Name = "KeyListener";
MainThread.Start();
ConsoleKeyListener.Start();
while (true)
{
if (Terminate)
{
Console.WriteLine("Terminating Process...");
MainThread.Abort();
ConsoleKeyListener.Abort();
Thread.Sleep(2000);
Thread.CurrentThread.Abort();
return;
}
if (stopProcessor)
{
Console.WriteLine("Ending Process...");
MainThread.Abort();
ConsoleKeyListener.Abort();
Thread.Sleep(2000);
Thread.CurrentThread.Abort();
return;
}
}
}
public static void ListerKeyBoardEvent()
{
do
{
if (Console.ReadKey(true).Key == ConsoleKey.Escape)
{
Terminate = true;
}
} while (true);
}
public static void startProcess()
{
int i = 0;
while (true)
{
if (!stopProcessor && !Terminate)
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Processing...." + i++);
Thread.Sleep(3000);
}
if(i==10)
stopProcessor = true;
}
}
}
Addressing cases that some of the other answers don't handle well:
Responsive: explicit/direct execution of keypress handling code; avoids the vagaries of polling or blocking delays
Optionality: global keypress is opt-in; by default the app exits normally (if no action)
Separation of concerns: less invasive listening code; operates independently of your console app's core logic.
Many of the solutions on this page involve polling Console.KeyAvailable or blocking on Console.ReadKey. While it's true that the .NET Console is not very cooperative here, you can use Task.Run to move towards a more modern Async mode of listening.
The main issue to be aware of is that, by default, your console thread isn't set up for Async operation--meaning that, when you fall out of the bottom of your main function, instead of awaiting Async completions, your AppDoman and process will end. A proper way to address this would be to use Stephen Cleary's AsyncContext to establish full Async support in your single-threaded console program. But for simpler cases, like waiting for a keypress, installing a full trampoline may be overkill.
The example below would be for a console program used in some kind of iterative batch file. In this case, when the program is done with its work, normally it should exit without requiring a keypress, and then we allow an optional key press to prevent the app from exiting. We can pause the cycle to examine things, possibly resuming, or use the pause as a known 'control point' at which to cleanly break out of the batch file.
static void Main(String[] args)
{
Console.WriteLine("Press any key to prevent exit...");
var tHold = Task.Run(() => Console.ReadKey(true));
// ... do your console app activity ...
if (tHold.IsCompleted)
{
#if false // For the 'hold' state, you can simply halt forever...
Console.WriteLine("Holding.");
Thread.Sleep(Timeout.Infinite);
#else // ...or allow continuing on (to exit)
while (Console.KeyAvailable)
Console.ReadKey(true); // flush/consume any extras
Console.WriteLine("Holding. Press 'Esc' to exit.");
while (Console.ReadKey(true).Key != ConsoleKey.Escape)
;
#endif
}
}
If you are using Visual Studio, then you can use "Start Without Debugging" in the Debug menu.
It will automatically write "Press any key to continue . . ." to the console for you upon completion of the application and it will leave the console open for you until a key is pressed.
with following code you can listen for Spacebar in middle of your console execution and pause until another key is pressed with additional of option of listening for Escape Key in order to breake the main loop.
static ConsoleKeyInfo cki = new ConsoleKeyInfo();
while(true) {
if (WaitOrBreak()) break;
//your main code
}
private static bool WaitOrBreak(){
if (Console.KeyAvailable) cki = Console.ReadKey(true);
if (cki.Key == ConsoleKey.Spacebar)
{
Console.Write("waiting..");
while (Console.KeyAvailable == false)
{
Thread.Sleep(250);Console.Write(".");
}
Console.WriteLine();
Console.ReadKey(true);
cki = new ConsoleKeyInfo();
}
if (cki.Key == ConsoleKey.Escape) return true;
return false;
}
According to my experience, in console apps the easiest way to read the last key pressed is as follows (Example with arrow keys):
ConsoleKey readKey = Console.ReadKey ().Key;
if (readKey == ConsoleKey.LeftArrow) {
<Method1> (); //Do something
} else if (readKey == ConsoleKey.RightArrow) {
<Method2> (); //Do something
}
I use to avoid loops, instead I write the code above within a method, and I call it at the end of both "Method1" and "Method2", so, after executing "Method1" or "Method2", Console.ReadKey().Key is ready to read the keys again.
Console.WriteLine("Hello");
var key = Console.ReadKey();
DateTime start = DateTime.Now;
bool gotKey = Console.KeyAvailable;
while ((DateTime.Now - start).TotalSeconds < 2)
{
if (key.Key == ConsoleKey.Escape)
{
Environment.Exit(0);
}
else if (key.Key == ConsoleKey.Enter)
{
break;
}
This question already has answers here:
Writing string at the same position using Console.Write in C# 2.0
(2 answers)
How can I get the position of the cursor in a console app?
(2 answers)
Closed 5 years ago.
I'm trying to create a console application that gives me an updating clock in the corner with the ability to give an input.
I've tried using multiple threads but it gives me weird errors.
My clock function:
public class Work
{
public void Count()
{
for (int i = 0; i < 100; i++)
{
DateTime date = DateTime.Now;
Console.SetCursorPosition(0, 1);
Console.Write(new string(' ', Console.WindowWidth));
Console.SetCursorPosition((Console.WindowWidth - 8) / 2, 0);
Console.Write(String.Format("{0:HH:mm:ss}", date));
Console.WriteLine();
if (i == 90)
{
i = 0;
}
else
{
// Continue
}
}
}
}
My main function:
class Program
{
public static void Main(string[] args)
{
Console.CursorVisible = false;
Work w = new Work();
Console.WriteLine("Main Thread Start");
ThreadStart s = w.Count;
Thread thread1 = new Thread(s);
thread1.Start();
int i = 2;
Console.SetCursorPosition(0, i);
i = i + 1;
Console.WriteLine("Input:");
string input = Console.ReadLine();
Console.WriteLine(input);
}
}
Does anybody know how I can achieve this, is there any possible way that I can write to a clock with a different cursor or something along the lines of that?
Try change your code like this
class Program
{
static void Main(string[] args)
{
Console.CursorVisible = false;
var w = new Work();
Console.WriteLine("Main Thread Start");
ThreadStart s = w.Count;
var thread1 = new Thread(s);
thread1.Start();
int i = 2;
Console.SetCursorPosition(0, i);
var format = "Input:";
Console.WriteLine(format);
Console.SetCursorPosition(format.Length + 1, i);
string input = Console.ReadLine();
Console.WriteLine(input);
}
}
public class Work
{
public void Count()
{
while (true)
{
Thread.Sleep(1000);
var originalX = Console.CursorLeft;
var originalY = Console.CursorTop;
Console.SetCursorPosition(0, 1);
Console.Write(new string(' ', Console.WindowWidth));
Console.SetCursorPosition((Console.WindowWidth - 8) / 2, 0);
Console.Write("{0:HH:mm:ss}", DateTime.Now);
Console.SetCursorPosition(originalX, originalY);
}
}
}
The main idea is to store original cursor position before drawing your clock and then renurn it back.
var originalX = Console.CursorLeft;
var originalY = Console.CursorTop;
Console.SetCursorPosition(originalX, originalY);
I'm trying to find a way of causing the program to not pause but for their to be a delay to execute certain tasks. I.e. I am trying to delay outputting 'Hello' to the console for 10 seconds for example, but the program will continue to execute the rest of the program.
Using TPL:
static void Main(string[] args)
{
Console.WriteLine("Starting at " + DateTime.Now.ToString());
Task.Run(() =>
{
Thread.Sleep(10000);
Console.WriteLine("Done sleeping " + DateTime.Now.ToString());
});
Console.WriteLine("Press any Key...");
Console.ReadKey();
}
output:
Starting at 2/14/2017 3:05:09 PM
Press any Key...
Done sleeping 2/14/2017 3:05:19 PM
just note that if you press a key before 10 seconds, it will exit.
There are 2 typical ways to simulate a delay:
an asynchronous task-like: Task.Delay
or a blocking activity: Thread.Sleep
You seem to refer to the first situation.
Here it is an example
public static void Main(string[] args)
{
Both();
}
static void Both() {
var list = new Task [2];
list[0] = PauseAndWrite();
list[1] = WriteMore();
Task.WaitAll(list);
}
static async Task PauseAndWrite() {
await Task.Delay(2000);
Console.WriteLine("A !");
}
static async Task WriteMore() {
for(int i = 0; i<5; i++) {
await Task.Delay(500);
Console.WriteLine("B - " + i);
}
}
Output
B - 0
B - 1
B - 2
A !
B - 3
B - 4
Start a new thread:
Task.Factory.StartNew(new Action(() =>
{
Thread.Sleep(1000 * 10); // sleep for 10 seconds
Console.Write("Whatever");
}));
You could use a combination of Task.Delay and ContinueWith methods:
Task.Delay(10000).ContinueWith(_ => Console.WriteLine("Done"));
You could use 'Thread.Sleep(10000);'
See:
https://msdn.microsoft.com/en-us/library/d00bd51t(v=vs.110).aspx
I'm trying to make a test to see if someone has certain skills.
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.");
}
}
}
}
}
}
The thread does not execute; I suspect it is because of the string product = Console.ReadLine(); bit. The second question of this quiz was 12x12, and you have 2 seconds to answer, except the thread that counted the two seconds wasn't executed... Why...? And if you know, how would I fix it?
You only created a thread. You should also start it.
System.Threading.Thread t = new System.Threading.Thread(timer);
t.Start();
Just wrote this down as an example of how you can check for how long time has passed without using a thread.
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();
Stopwatch is another alternative: http://www.dotnetperls.com/stopwatch
If you really want threads (which are overkill for this problem) there are some good examples here: https://msdn.microsoft.com/en-us/library/ts553s52(v=vs.110).aspx
The two answers are on the spot, so let me just add how you can create a timer that's not as convoluted :)
var timeIsUp = false;
var timer = new Timer(_ => { timeIsUp = true; }, null, 5000, Timeout.Infinite);
But in general, #JensB is absolutely right - using multi-threading should be the last option. It's very hard to handle multi-threading properly, so avoiding it is a pretty decent strategy. The Timer example I've shown is also multi-threaded - the callback on the timer will occur on a different thread. This introduces synchronization issues, but they shouldn't be too painful for a simple case like this. To improve upon this, you'd at least want to ensure the local is updated safely:
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) ...
}
Finally, using Thread manually is completely unnecessary nowadays. It's much easier to use Tasks for concurrency and multi-threading. For example:
var timerTask = Task.Delay(5000);
var answer = Console.ReadLine();
if (timerTask.IsCompleted) Console.WriteLine("Too late");
The best option IMO would be to use proper asynchronous APIs - sadly, the .NET Console class doesn't have those. As silly as it is, it seems that this is a pretty decent option:
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;
}
The interesting point about this approach is that I can exit the application (and abort the input) even if the user doesn't press enter. It also allows you to tie together complex work flows using await, although that's slightly tricky in a console application.
The helper method ReadLineFromConsole actually works the same as the usual ReadLine method, however, it also checks for cancellation, and to prevent it from "stealing" data from later ReadLine calls, it will Peek first. This doesn't make it thread-safe - you still shouldn't using multiple readlines at the same time from different threads. But it does mean that we can ignore the output when it finally comes. Bear in mind that the thread will be waiting all this time until a console input comes - do not use this to launch multiple simultaneous requests without ensuring there's some input on the way eventually (e.g. using the usual Console.ReadLine in between the ReadLineFromConsole calls etc.).
Some refactoring to your code and solution to your problem :
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();
}
}
}
}
The idea: create a Task that prints an increasing number of asterisks. When the user presses Enter, the Task prints 10 asterisks and then stops.
The code:
namespace CancellingLongRunningTasks
{
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main()
{
var cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
Task task = Task.Run(() =>
{
int count = 1;
while (!token.IsCancellationRequested)
{
Console.WriteLine(new string('*', count));
Thread.Sleep(1000);
count++;
}
}, token).ContinueWith(
parent =>
{
var count = 10;
while (count > 0)
{
Console.WriteLine(new string('*', count));
Thread.Sleep(1000);
count--;
}
}, TaskContinuationOptions.OnlyOnCanceled);
Console.WriteLine("Press enter to stop the task.");
if (Console.ReadLine().Contains(Environment.NewLine))
{
cancellationTokenSource.Cancel();
task.Wait();
}
}
}
}
The question: why isn't my continuation task executed?
It isn't being executed because you're not actively cancelling the Task, only checking if a cancellation was requested. Use CancellationToken.ThrowIfCancellationRequested which throws an OperationCanceledException and will transform the Task into a cancelled state or simply throw the exception with the corresponding token:
Task task = Task.Run(() =>
{
int count = 1;
while (!token.IsCancellationRequested)
{
Console.WriteLine(new string('*', count));
Thread.Sleep(1000);
count++;
}
token.ThrowIfCancellationRequested();
}, token)
Which is equivalent to:
Task task = Task.Run(() =>
{
int count = 1;
while (!token.IsCancellationRequested)
{
Console.WriteLine(new string('*', count));
Thread.Sleep(1000);
count++;
}
throw new OperationCanceledException(token);
}, token)