I am just starting to use multi-threading, and I am trying to build a console application that starts to count with 2 different threads when the user hits the Enter key, and stops counting when the Enter key is pressed a second time, and then outputs the count for both threads (I realize that with my code, these will be 2 different numbers). I set this up, but for some reason, one of the numbers is usually negative.There is also quite a bit of a delay before I receive count output. Why am I getting negative counts, why is the delay so long, and how can I correct this?
My code so far;
class Program
{
static void Main(string[] args)
{
System.Threading.Thread threadA = new Thread(ThreadA);
System.Threading.Thread threadB = new Thread(ThreadB);
Console.WriteLine("Once you press enter, this application will count as high as it can until you press enter again.");
ConsoleKeyInfo info = Console.ReadKey();
if (info.Key == ConsoleKey.Enter)
{
threadA.Start();
threadB.Start();
}
}
private static bool continueCounting = true;
static void ThreadA()
{
int count = 0;
for (int i = 0; i < int.MaxValue ; i++)
{
count++;
}
ConsoleKeyInfo info2 = Console.ReadKey();
if (info2.Key == ConsoleKey.Enter)
continueCounting = false;
Console.WriteLine(count);
}
static void ThreadB()
{
int count = 0;
while (continueCounting)
{
count++;
}
Console.WriteLine(count);
Console.ReadLine();
}
}
one of the numbers is usually negative
That's because there was no limit guard on the ThreadB loop. It was entirely possible for count to reach MaxValue then wrap around to negative maxvalue.
I also put all keyboard checks in your main thread where they arguably should be. It is here that we set continueCounting to false when the second enter key is pressed.
I also made continueCounting volatile as it is being used by multiple threads and its value should not be CPU optimised/cached.
Try this code, this fixes the delay you were experiencing; allows both threads to count at once; and exit ASAP when the enter key is pressed.
class Program
{
#region Static fields
private static volatile bool continueCounting = true;
#endregion
#region Methods
static void Main(string[] args)
{
var threadA = new Thread(ThreadA);
var threadB = new Thread(ThreadB);
Console.WriteLine(
"Once you press enter, this application will count as high as it can until you press enter again.");
var info = Console.ReadKey();
if (info.Key == ConsoleKey.Enter)
{
threadA.Start();
threadB.Start();
}
info = Console.ReadKey();
if (info.Key == ConsoleKey.Enter)
{
continueCounting = false;
}
Console.ReadLine();
}
static void ThreadA()
{
var count = 0;
for (var i = 0; i < int.MaxValue && continueCounting; i++)
{
count++;
}
Console.WriteLine($"A: {count}");
}
static void ThreadB()
{
var count = 0;
while (continueCounting && count < int.MaxValue)
{
count++;
}
Console.WriteLine($"B: {count}");
}
#endregion
}
Related
I've been trying to get a button mashing minigame working in my RPG, but I haven't managed to get it to work. This is the code I've created:
using System;
namespace CounterAttack
{
class Program
{
public static int n = 0;
public static int buttonmash = 0;
public static string buttonpressed;
public static void CounterAttack()
{
n = 0;
while (n < 4000)
{
buttonpressed = Console.ReadKey().ToString();
System.Threading.Thread.Sleep(1);
n = n + 1;
if (buttonpressed == " ")
{
buttonmash = buttonmash + 1;
buttonpressed = "a";
}
else
{
buttonpressed = "a";
}
}
Console.WriteLine($"You pressed spacebar {buttonmash} times ");
}
static void Main(string[] args)
{
CounterAttack();
System.Threading.Thread.Sleep(1000);
Console.ReadKey();
}
}
}
For reasons unknown to me, the program will not exit the while loop. This means I can't check if the other parts of code will work at all.
So my question is: How can I alter this code to read the amount of times spacebar has been pressed in 4 seconds?
This sort-of works, so long as you press the space bar after the 4 second timer is up.
static void Main(string[] args)
{
var startTime = DateTime.Now;
var n = 0;
while (DateTime.Now < startTime.AddSeconds(4))
{
var key = Console.ReadKey();
System.Threading.Thread.Sleep(1);
if (key.Key == ConsoleKey.Spacebar) n = n + 1;
}
Console.WriteLine($"You pressed spacebar {n} times ");
}
"I've been trying to get a button mashing minigame working in my RPG, but I haven't managed to get it to work."
You are doing this in a console application. So the answer is on a spectrum from "not easily" to "plain impossible". I hope somebody else can give you answer with more precision along that axis.
Game development and the GUI technologies do not mix that well to begin with. That old Solitaire game was pretty much the upper limit of what games you can do in them. Console matches it even less then that.
All the serious game development is done in any enviroment that comes with a game loop. XNA is a bit dated, but for .NET Core there is some options: https://www.microsoft.com/net/apps/gaming
Try this :
class Program
{
private static int numSpaces = 0;
static void Main(string[] args)
{
Thread th = new Thread(CheckSpace);
th.Start();
th.Join();
Console.WriteLine($"You pressed space {numSpaces} times");
Console.Read();
}
private static void CheckSpace()
{
Stopwatch sw = new Stopwatch();
sw.Start();
while (true)
{
if (Console.KeyAvailable)
{
if (Console.ReadKey().Key == ConsoleKey.Spacebar)
{
numSpaces++;
}
}
if (sw.ElapsedMilliseconds>= 4000)
{
break;
}
Thread.Sleep(10);
}
}
}
#auberg's solution is good, but you really don't need a second thread. Yeah, calling Thread.Sleep() on the main thread is a bad idea, but doing it for such a short time is un-noticeable anyway.
Try this:
public static void CountKeyPresses()
{
Console.WriteLine("OK, start pressing the space bar");
var numPresses = 0;
var stopWatch = new Stopwatch();
stopWatch.Start();
while (stopWatch.ElapsedMilliseconds < 4000)
{
if (Console.KeyAvailable && Console.ReadKey().Key == ConsoleKey.Spacebar)
{
++numPresses;
}
System.Threading.Thread.Sleep(10);
}
Console.WriteLine($"\r\nYou pressed the spacebar {numPresses} times");
}
I am creating a random number guessing game where the user sets the maximum number they want to guess from. I have figured out most of the code, the problem that I seem to be getting is that after setting the maximum number and beginning to guess the answer is always 0. Any tips on how to adjust my code so that the answer is a number within the range and not 0?
class Program
{
public static int SelectedNumber = 0;
public static Random ran = new Random();
public static bool GameOver = false;
public static int UserMaxValue = 0;
public static string decision;
static void Main(string[] args)
{
int UserNumber;
SelectedNumber = ran.Next(0, UserMaxValue);
do
{
Console.WriteLine("What is the maximum number you want to guess from?");
UserMaxValue = Convert.ToInt32(Console.ReadLine());
do
{
Console.WriteLine("Select a number between 1 and {0}!", UserMaxValue);
UserNumber = Convert.ToInt32(Console.ReadLine());
GuessNumber(UserNumber);
} while (GameOver == false);
} while (GameOver == false);
}
public static void GuessNumber(int UserNumber)
{
if (UserNumber < SelectedNumber)
Console.WriteLine("Your number is wrong, please try again!");
else if (UserNumber > SelectedNumber)
Console.WriteLine("Your Number is wrong, please try again!");
//else if (UserNumber == 0)
// Console.WriteLine("Your Number is wrong, please try again!");
else
{
Console.WriteLine("Yay! You got the right number. Do you want to play again? (y/n) ", decision);
decision = Console.ReadLine();
if (decision == "n")
GameOver = true;
else
do
{
Console.WriteLine("What is the maximum number you want to guess from?");
UserMaxValue = Convert.ToInt32(Console.ReadLine());
do
{
Console.WriteLine("Select a number between 1 and {0}!", UserMaxValue);
UserNumber = Convert.ToInt32(Console.ReadLine());
GuessNumber(UserNumber);
} while (GameOver == false);
} while (GameOver == false);
}
}
}
}
You have the following lines, in this order (I'm omitting the rest in between):
public static int UserMaxValue = 0;
// ...
SelectedNumber = ran.Next(0, UserMaxValue);
// ...
Console.WriteLine("What is the maximum number you want to guess from?");
UserMaxValue = Convert.ToInt32(Console.ReadLine());
You have to ask the user for UserMaxValue before you can correctly set SelectedNumber.
Had to do some corrections on your code. Added some comments for the changes.
You better add error handling on the user input.
UPDATE: Added error handling for invalid input.
class Program
{
public static int SelectedNumber = 0;
public static Random ran = new Random();
public static bool GameOver = false;
public static int UserMaxValue = 0;
static void Main(string[] args)
{
int UserNumber = 0;
bool playAgain = false;
do
{
bool isUserMaxValueValid = false;
do
{
Console.WriteLine("What is the maximum number you want to guess from?");
isUserMaxValueValid = int.TryParse(Console.ReadLine(), out UserMaxValue);
} while (!isUserMaxValueValid);
SelectedNumber = ran.Next(1, UserMaxValue); // Re-assign SelectedNumber for every new max number input. Random number start changed to 1 to avoid 0 value for SelectedNumber
do
{
bool isUserNumberValid = false;
do
{
Console.WriteLine("Select a number between 1 and {0}!", UserMaxValue);
isUserNumberValid = int.TryParse(Console.ReadLine(), out UserNumber);
} while (!isUserNumberValid);
playAgain = GuessNumber(UserNumber);
// I changed GameOver to see if the guessing portion is finished or not
} while (!GameOver); // You don't need to use GameOver == false
} while (playAgain); // Check if user wants to play again or not
}
public static bool GuessNumber(int UserNumber)
{
if (UserNumber != SelectedNumber)
{
GameOver = false;
Console.WriteLine("Your number is wrong, please try again!");
}
else
{
GameOver = true;
bool isPlayAgainValid = false;
Console.WriteLine("Yay! You got the right number. Do you want to play again? (y/n)");
do
{
string decision = Console.ReadLine();
if (decision.Equals("n", StringComparison.InvariantCultureIgnoreCase))
{
return false;
}
else if (decision.Equals("y", StringComparison.InvariantCultureIgnoreCase))
{
break;
}
if(!isPlayAgainValid)
{
Console.WriteLine("Please enter y or n only.");
}
} while (!isPlayAgainValid);
// I removed redundant code
}
return true;
}
}
I have a function that reads numbers from console and writes them to queue and a massive of threads that should read numbers from queue, calculate factorials and write it to file.
static void Main(string[] args)
{
int threadsNumber = 0;
Console.WriteLine("Enter the number of threads: ");
while ((Int32.TryParse(Console.ReadLine(), out threadsNumber) == false)||(threadsNumber <= 0))
{
Console.WriteLine("You need to enter a positive number!");
}
Console.Clear();
Queue<int> numbersQueue = new Queue<int>();
Thread [] threadMas = new Thread[threadsNumber];
for (int i = 0; i < threadsNumber; i++)
{
threadMas[i] = new Thread(() => ThreadProc.ThreadProcStart(numbersQueue));
threadMas[i].Start();
}
//some code...
ThreadProc:
class ThreadProc
{
private static object locker = new object();
public static void ThreadProcStart(Queue<int> queue)
{
while (true)
{
int num = Deq(queue); //Checks if queue is empty. If not -
//Returns first element
if (num != -1)
{
BigInteger bigInt = Factorial.FactTree(num); //factorical
Writer.WriteToFile(num + " factorial: " + bigInt, "result.txt"); //write to file
}
else
{
// Here I need to add something
Thread.Sleep(0);
}
}
}
public static int Deq(Queue<int> queue)
{
lock (locker)
{
if (queue.Count != 0)
{
return queue.Dequeue();
}
return -1;
}
}
}
I need to stop all threads when all calculations are done. How can i mark thread as "unactive" when the queue is empty without actually aborting it?
Use BlockingCollection, which underneath the covers uses ConcurrentQueue. Your code will be soooo much simpler and you won't have to worry about subtle locking bugs.
Your code would then look something like this:
static void Main(string[] args)
{
int threadsNumber = 0;
Console.WriteLine("Enter the number of threads: ");
while ((Int32.TryParse(Console.ReadLine(), out threadsNumber) == false) || (threadsNumber <= 0))
{
Console.WriteLine("You need to enter a positive number!");
}
Console.Clear();
BlockingCollection<int> queue = new BlockingCollection<int>();
Thread[] threadMas = new Thread[threadsNumber];
for (int i = 0; i < threadsNumber; i++)
{
threadMas[i] = new Thread(ProcessQueue);
threadMas[i].Start(queue);
}
string userInput = null;
do
{
Console.WriteLine("Enter a number: ");
userInput = Console.ReadLine();
if (userInput != "stop") {
queue.Add(int.Parse(userInput)); // These numbers get process by the threads in parallel.
}
} while (userInput != "stop");
queue.CompleteAdding(); // Use this to signal that no more items will be added to the queue.
// Now the main thread exits.
// But the program stays alive until the other threads finish
// clearing the queue.
}
private static void ProcessQueue(object data)
{
BlockingCollection<int> queue = (BlockingCollection<int>)data;
foreach (var num in queue.GetConsumingEnumerable()) // This will automatically exit the loop once CompleteAdding is called and the queue is empty.
{
BigInteger bigInt = Factorial.FactTree(num); //factorical
Writer.WriteToFile(num + " factorial: " + bigInt, "result.txt"); //write to file
}
}
I try to synchronize three threads (named "1", "2" and "3") using Semaphore class. They have to print a string into console respecting consequence: 1->2->3. Here is my code:
class MyThread
{
public Thread Thrd;
static Semaphore sem = new Semaphore(1, 1);
static int flag = 1;
public MyThread(string name)
{
Thrd = new Thread(this.Run);
Thrd.Name = name;
Thrd.Start();
}
void Run()
{
sem.WaitOne();
if (Convert.ToInt32(Thrd.Name) == flag)
{
Console.WriteLine("Thread " + Thrd.Name);
flag++;
}
if (flag == 4)
flag = 1;
Thread.Sleep(300);
sem.Release();
}
}
class SemaphoreDemo
{
static void Main()
{
for (int i = 0; i < 10; i++)
{
MyThread mt1 = new MyThread("1");
MyThread mt2 = new MyThread("2");
MyThread mt3 = new MyThread("3");
mt1.Thrd.Join();
mt2.Thrd.Join();
mt3.Thrd.Join();
}
}
}
But sometimes strings from threads #2 and #3 are not seen. Where is my mistake and how can I fix this problem?
Thank you very much!
The problem is that sometimes a thread will acquire the semaphore out of order, and you don't have any retry logic. Take a look at your Run method.
void Run()
{
sem.WaitOne();
if (Convert.ToInt32(Thrd.Name) == flag)
{
Console.WriteLine("Thread " + Thrd.Name);
flag++;
}
if (flag == 4)
flag = 1;
Thread.Sleep(300);
sem.Release();
}
Now, what happens if the thread named "3" acquires the semaphore first? flag is equal to 1, so the conditional code won't be executed. The thread will just sleep for 300 milliseconds and then exit. If you want this to work, you have to make the thread retry:
void Run()
{
bool success = false;
while (!success)
{
sem.WaitOne();
if (Convert.ToInt32(Thrd.Name) == flag)
{
Console.WriteLine("Thread " + Thrd.Name);
flag++;
success = true;
}
sem.Release();
if (!success)
{
// let somebody else try
Thread.Sleep(300);
}
}
}
That will make your sample work as expected.
I suspect that this is just an exercise to see how threading and semaphores work. Note, however, that a Mutex is usually more appropriate than a Semaphore with a maximum count of 1.
Note also that there are other ways to make threads execute in sequence, although truthfully if you're going to sequence your threads then you probably don't need more than one thread. Unless those threads are doing other things and they only need to sequence once--or infrequently.
I've made this producer-consumer sample, but I don't know why it freezes at the end.
Where is the problem? If I put a breakpoint at line setNum(-99); and then after break continue it finishes OK.
Please also tell me if this code is ok and threadsafe. It must work like that, so meanwhile consumer is processing its given value, all other values from producer must be ignored.
I am very new to multithreading.
class Program
{
delegate void SetNumberDelegate(int number);
static void Main(string[] args)
{
Random rnd = new Random();
ConsumerClass consumerClass = new ConsumerClass();
SetNumberDelegate setNum = new SetNumberDelegate(consumerClass.setNumber);
Thread.Sleep(20);
int num;
int count = 0;
Console.WriteLine("Start");
while (count++ < 100)
{
num = rnd.Next(0, 100);
Console.WriteLine("Generated number {0}", num);
if (num > 30)
{
setNum(num);
}
}
setNum(-99);
Console.WriteLine("End");
Console.ReadKey();
}
}
class ConsumerClass : IDisposable
{
private int number;
private object locker = new object();
private EventWaitHandle _wh = new AutoResetEvent(false);
private Thread _consumerThread;
public ConsumerClass()
{
number = -1;
_consumerThread = new Thread(consumeNumbers);
_consumerThread.Start();
}
public void Dispose()
{
setNumber(-99);
_consumerThread.Join();
_wh.Close();
}
public void setNumber(int num)
{
if (Monitor.TryEnter(locker))
{
try
{
number = num;
Console.WriteLine("Setting number {0}", number);
}
finally
{
// Ensure that the lock is released.
Monitor.Exit(locker);
}
_wh.Set();
}
}
public void consumeNumbers()
{
while (true)
{
Monitor.Enter(locker);
if (number > -1)
{
try
{
Console.WriteLine("Processing number:{0}", number);
// simulate some work with number e.g. computing and storing to db
Thread.Sleep(20);
Console.WriteLine("Done");
number = -1;
}
finally
{
Monitor.Exit(locker);
}
}
else
{
if (number == -99)
{
Console.WriteLine("Consumer thread exit");
return;
}
Monitor.Exit(locker);
_wh.WaitOne(); // No more tasks - wait for a signal
}
}
}
}
Rewrite setNumber like this to see your problem:
public void setNumber(int num) {
if (Monitor.TryEnter(locker)) {
// etc..
}
else Console.WriteLine("Number {0} will never make it to the consumer", num);
}
You'll have to block, waiting for the consumer to be ready to consume or use a queue.
Monitor.TryEnter(locker); will usually fail (including for -99) so you aren't going to set a fair number of the values, which is why the output is lacking in setting statements. This is because it will not wait to acquire the lock it will just return false.
The problem appears to be in the last part of the code. You're holding the lock when you execute this:
else
{
if (number == -99)
{
Console.WriteLine("Consumer thread exit");
return;
}
Monitor.Exit(locker);
_wh.WaitOne(); // No more tasks - wait for a signal
}
So if number == 99, the method returns without releasing the lock.
Your ConsumeNumbers method is overly complex. You can simplify it:
while (true)
{
_wh.WaitOne();
lock (locker)
{
if (number == -99)
break;
if (number > -1)
{
// process the number.
number = -1;
}
}
}
That will do the same thing, and is much simpler code.
By the way, the construct:
lock (locker)
{
// do stuff here
}
is the same as:
Monitor.Enter(locker);
try
{
// do stuff here
}
finally
{
Monitor.Exit(locker);
}