• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 416
  • Last Modified:

Multithreaded console

Hey!

I want to create a simple multithreaded application in the console
that will display some data while reading data from the user
However I have ran into some trouble.

If one of my threads calls Console.Writeline(), both what the user has typed in and what my program typed in will be displayed at once.

is there any way to store what the user has typed in, display the message
then restore it back to the console?
0
freefrag
Asked:
freefrag
  • 9
  • 8
  • 4
1 Solution
 
Todd GerbertIT ConsultantCommented:
Can you post your code?  Sounds like you either need some thread synchronization, or are echoing characters back to the console in addition to .Net also echoing the typed characters.
0
 
freefragAuthor Commented:
static void Main(string[] args)
        {
            TextWriter txt = null;
           
            Thread Spam = new Thread(new ThreadStart(SPPPPPAM));
            Thread Read = new Thread(new ThreadStart(READDDD));
            Read.Start();
            Spam.Start();
           
        }
        static void READDDD()
        {
            while (true)
            {
               
                string s = string.Format("rrright, so thats what you ment: {0}", Console.ReadLine());
                Console.WriteLine(s);
            }
        }
        static void SPPPPPAM()
        {
            while (true)
            {
                Console.WriteLine("Spammin");
                Thread.Sleep(1000);
            }
           
        }
0
 
deepu chandranCommented:
HI,

Try using Lock like below,

static void READDDD()
        {
            private System.Object lockThis = new System.Object();

            while (true)
            {
               Lock(lockThis)
               {
string s = string.Format("rrright, so thats what you ment: {0}",  Console.ReadLine());
                Console.WriteLine(s);
                }
            }
        }
refer this msdn link
http://msdn.microsoft.com/en-us/library/ms173179%28VS.80%29.aspx
Regards
Deepu
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
freefragAuthor Commented:
doesnt work probably because the console buffer is a shared resource
0
 
Todd GerbertIT ConsultantCommented:
I believe both threads need to refer to the same object in order for locking to work, so something like this (sorry, not exactly sure how you intended this to run, or what you're trying to accomplish, but this does demonstrate thread sync using a lock):


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace ConsoleApplication2
{
	class Program
	{
		static Object lckObj = new Object();
 
		static void Main(string[] args)
		{
			
 
			Thread Spam = new Thread(new ThreadStart(SPPPPPAM));
			Thread Read = new Thread(new ThreadStart(READDDD));
			Read.Start();
			Spam.Start();
 
		}
		static void READDDD()
		{
			
 
			while (true)
			{
				lock (lckObj)
				{
					Console.Write("Enter some text: ");
					string s =  Console.ReadLine();
					Console.WriteLine("You entered {0}", s);
				}
			}
		}
		static void SPPPPPAM()
		{
			while (true)
			{
				lock (lckObj)
				{
					Console.WriteLine("Spammin");
				}
				Thread.Sleep(1000);
			}
 
		}
	}
}

Open in new window

0
 
freefragAuthor Commented:
nope, still isnt working.

Now it doesnt type the text until the user presses enter
0
 
deepu chandranCommented:
0
 
Todd GerbertIT ConsultantCommented:
Well, like I said I'm not sure how you intended for this to run.  The code in your question is kinda like two different programs both running simultaneously on the same console with complete disregard for each other, i.e. if the 1,000ms Sleep() in thread 2 happens to expire while the user is entering text into thread 1's ReadLine() it's not gonna know, or care, and will happily insert "spammin" into whatever the user is typing.

My example uses the lock, so that 1 thread waits for the other, before thread 1 attempts to read anything from the user it locks lckObj, which causes thread 2 to wait until the lock is released - which happens after Console.ReadLine(), i.e. when the user presses enter - this way thread 2 is not inserting text into the console while thread 1 is trying to have the user enter input.  Because thread 1 has no delay, as soon as a user enters text it's prompting for another line, it has the effect of alternating lines of user text & "spammin."

Put a 5 second delay in the READDD loop.
0
 
Todd GerbertIT ConsultantCommented:
Add Thread.Sleep(5000) in READDD thread *outside* the lock block.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace ConsoleApplication2
{
	class Program
	{
		static Object lckObj = new Object();
 
		static void Main(string[] args)
		{
 
 
			Thread Spam = new Thread(new ThreadStart(SPPPPPAM));
			Thread Read = new Thread(new ThreadStart(READDDD));
			Read.Start();
			Spam.Start();
 
		}
		static void READDDD()
		{
 
 
			while (true)
			{
				lock (lckObj)
				{
					Console.Write("Enter some text: ");
					string s = Console.ReadLine();
					Console.WriteLine("You entered {0}", s);
				}
				Thread.Sleep(5000);
			}
		}
		static void SPPPPPAM()
		{
			while (true)
			{
				lock (lckObj)
				{
					Console.WriteLine("Spammin");
				}
				Thread.Sleep(1000);
			}
 
		}
	}
}

Open in new window

0
 
Todd GerbertIT ConsultantCommented:
Maybe this is what you wanted.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace ConsoleApplication3
{
	class Program
	{
		static Object lckObj = new Object();
 
		static void Main(string[] args)
		{
			Thread inputThread = new Thread(InputThreadProc);
			Thread outputThread = new Thread(OutputThreadProc);
 
			inputThread.Start();
			outputThread.Start();
		}
 
		static void InputThreadProc()
		{
			StringBuilder userInput = new StringBuilder();
 
			while (true)
			{
				if (Console.KeyAvailable)
				{
					char inputChar = Console.ReadKey(true).KeyChar;
					if ((inputChar == '\r') || (inputChar == '\n'))
					{
						lock (lckObj)
						{
							Console.WriteLine("User entered: {0}", userInput.ToString());
							userInput.Remove(0, userInput.Length);
						}
					}
					else
						userInput.Append(inputChar);
				}
				else
					Thread.Sleep(125);
			}
		}
 
		static void OutputThreadProc()
		{
			string outputText = "Hello World";
 
			while (true)
			{
				lock (lckObj)
				{
					Console.WriteLine(outputText);
				}
				Thread.Sleep(1000);
			}
		}
	}
}

Open in new window

0
 
freefragAuthor Commented:
still not working.

it stops to let you type in the text, and this is no what I want it to do.

I want the whole program to continute Writing to the console, but allowing the user to input text at the same time. so for example the current output is

Spam
Spam
Spam
Spam
asd

where asd is what the user typed in.
If at the same time the other thread writes a line this is what will happen

Spam
Spam
Spam
Spam
asdSpam

and what I want it to do is

Spam
Spam
Spam
Spam
Spam
asd

so that it adds a line and doesnt delete what the user typed in
0
 
Todd GerbertIT ConsultantCommented:
In my most recent snippet, on line 30, change true to false.  This will cause the users keystrokes to be displayed on-screen admidst all the "Spammin's", and once the user presses the Enter key, a clean line is output.
0
 
deepu chandranCommented:
Can you Just try a Console.Clear() just after the Second Thread.

0
 
deepu chandranCommented:
Sorry I mean Console.Out.Flush()
0
 
Todd GerbertIT ConsultantCommented:
This is the type of output you're expecting?  Note that the bold characters are what I typed, and notice how - since both threads are using the console simultaneously - "Hello World" occurs in the middle of whatever I was typing, until I press enter then thread 1 causes thread 2 to pause, outputs whatever I entered, then lets thread 2 resume.

Hello World
ThHello World
e quickHello World
brownHello World
fox juHello World
mped oveHello World
r the laHello World
zy dogHello World
User entered: The quick brown fox jumped over the lazy dog.
Hello World
Hello World
0
 
freefragAuthor Commented:
no this is what I was expecting

h

//new line

HelloWorld
hey

//new line

HelloWorld
HelloWorld
 hey there

//new line

HelloWorld
HelloWorld
HelloWorld
 hey there!

//user pressed enter


HelloWorld
HelloWorld
HelloWorld
 You typed in: hey there!
0
 
Todd GerbertIT ConsultantCommented:
Hmm, this code seems a little overly complex to me, but I think it does what you want. ;)

Hello World
Hello World
Th
Hello World
The
Hello World
Hello World
The quick
Hello World
The quick brow
Hello World
The quick brown
Hello World
Hello World
Hello World
The quick brown fox ju
Hello World
The quick brown fox jumped ov
Hello World
The quick brown fox jumped over the la
Hello World
The quick brown fox jumped over the lazy do
Hello World
User entered: The quick brown fox jumped over the lazy dog.
Hello World
Hello World
Some
Hello World
Something
Hello World
Something else
Hello World
Something else.
Hello World
User entered: Something else.
Hello World
Hello World
Hello World
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace ConsoleApplication3
{
	class Program
	{
		static Object consoleOutputLock = new Object();
		static Object boolLock = new Object();
		static Object stringLock = new Object();
 
		static bool newKeysEntered = false;
		static StringBuilder userInput = new StringBuilder();
 
		static void Main(string[] args)
		{
			Thread inputThread = new Thread(InputThreadProc);
			Thread outputThread = new Thread(OutputThreadProc);
 
			inputThread.Start();
			outputThread.Start();
		}
 
		static void InputThreadProc()
		{
 
			while (true)
			{
				if (Console.KeyAvailable)
				{
					char inputChar = Console.ReadKey(true).KeyChar;
					if ((inputChar == '\r') || (inputChar == '\n'))
					{
						lock (boolLock) { newKeysEntered = false; }
						lock (consoleOutputLock)
						{
							lock (stringLock)
							{
								Console.WriteLine("User entered: {0}", userInput.ToString());
								userInput.Remove(0, userInput.Length);
							}
						}
					}
					else
					{
						lock (boolLock) { newKeysEntered = true; }
						lock (stringLock) { userInput.Append(inputChar); }
					}
				}
			}
		}
 
		static void OutputThreadProc()
		{
			string outputText = "Hello World";
			bool alsoOutputUserText = false;
 
			while (true)
			{
				lock (boolLock) { alsoOutputUserText = newKeysEntered; }
				lock (consoleOutputLock)
				{
					if (alsoOutputUserText)
					{
						lock (boolLock) { newKeysEntered = false; }
						lock (stringLock) { Console.WriteLine(userInput.ToString()); }
					}
					Console.WriteLine(outputText);
				}
				Thread.Sleep(1000);
			}
		}
	}
}

Open in new window

0
 
freefragAuthor Commented:
still not what I mean :/

instead of it displaying

Hello World
Some
Hello World
Something
Hello World
Something else
Hello World
Something else.
Hello World
User entered: Something else.
Hello World
Hello World
Hello World

I dont want to show what the user entered previously.

I just want the input to always be on the bottom of the console, regardless of how many lines the user entered.

Think of it as an internet messenger.
0
 
freefragAuthor Commented:
Maybe there is a way, to see and edit what the user types before he presses enter?
0
 
Todd GerbertIT ConsultantCommented:
Geez, picky, aren't we? ;)

Maybe this will get down the right path.  I added the WriteToConsole function (which the worker threads use in lieu of Console.Write/WriteLine); it makes use of Console.CursorTop, Console.WindowHeight and Console.SetCursorPosition to control where in the console window text is written - either to the very bottom line (if WriteBottomLine is true), or to the top 24 lines (WriteBottomLine is false).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
 
namespace ConsoleApplication3
{
	class Program
	{
		static Object consoleOutputLock = new Object();
		static Object boolLock = new Object();
		static Object stringLock = new Object();
 
		static bool newKeysEntered = false;
		static StringBuilder userInput = new StringBuilder();
		static string bottomLineText = String.Empty;
 
		static void Main(string[] args)
		{
			Thread inputThread = new Thread(InputThreadProc);
			Thread outputThread = new Thread(OutputThreadProc);
 
			inputThread.Start();
			outputThread.Start();
		}
 
		static void InputThreadProc()
		{
 
			while (true)
			{
				if (Console.KeyAvailable)
				{
					char inputChar = Console.ReadKey(true).KeyChar;
					if ((inputChar == '\r') || (inputChar == '\n'))
					{
						lock (boolLock) { newKeysEntered = false; }
						lock (consoleOutputLock)
						{
							lock (stringLock)
							{
								WriteToConsole("User pressed ENTER: " + userInput.ToString(), true);
								userInput.Remove(0, userInput.Length);
							}
						}
					}
					else
					{
						lock (boolLock) { newKeysEntered = true; }
						lock (stringLock) { userInput.Append(inputChar); }
					}
				}
			}
		}
 
		static void OutputThreadProc()
		{
			string outputText = "Hello World";
			bool alsoOutputUserText = false;
 
			while (true)
			{
				lock (boolLock) { alsoOutputUserText = newKeysEntered; }
				lock (consoleOutputLock)
				{
					if (alsoOutputUserText)
					{
						lock (boolLock) { newKeysEntered = false; }
						lock (stringLock) { WriteToConsole(userInput.ToString() + "\r\n", true); }
					}
 
					WriteToConsole(outputText + "\r\n", false);
				}
				Thread.Sleep(1000);
			}
		}
 
		static void WriteToConsole(string TextToWrite, bool WriteBottomLine)
		{
			if (WriteBottomLine)
			{
				bottomLineText = TextToWrite;
				int prevCursorPos = Console.CursorTop;
				Console.SetCursorPosition(0, Console.WindowHeight - 1);
				Console.Write(TextToWrite);
				Console.SetCursorPosition(0, prevCursorPos);
			}
			else
			{
				if (Console.CursorTop >= Console.WindowHeight - 2)
				{
					Console.Clear();
					Console.SetCursorPosition(0, 0);
					if (bottomLineText.Length > 0)
						WriteToConsole(bottomLineText, true);
				}
 
				Console.Write(TextToWrite);
			}
		}
	}
}

Open in new window

0
 
freefragAuthor Commented:
well this isnt exactly what I wanted, but fair enough. You've done waaaay too much work for this to go unnoticed. You helped me realise what question I need to ask :)

thanks a lot
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 9
  • 8
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now