views:

185

answers:

7

I am just learning how to work with threading mechanism in C#. I took a example from msdn and altered it to design a simple game like this. But the problem is the while loop in DoWork method is reading keys even before DoWork Method is called from main program. I,e. When you run the program before "Go:" appears on the screen if you type some thing while loop in DoWork method is reading keys. But control should be passed to while loop after printing "Go:" on screen, Right. Can someone kindly explain me why it is happening like this. Thank You.

 public class Worker    
{
  ConsoleKeyInfo cki;  
  StringBuilder sb = new StringBuilder();
  bool f = true;

// This method will be called when the thread is started.
public void DoWork()
{
    Console.SetCursorPosition(49, 8);
    Console.Write("Go:");
    Console.SetCursorPosition(53, 8);

    while (!_shouldStop)
    {         
       cki = Console.ReadKey(true);
       if (f == true && (65 <= Convert.ToInt32(cki.Key) && Convert.ToInt32(cki.Key) <= 90))
       {
           Console.Write(cki.Key.ToString());               
           sb.Append(cki.Key.ToString());
       }  
    }
    while (true)
    {
        if (cki.Key.ToString() == "Enter") break;
        cki = Console.ReadKey(true);
        if (cki.Key.ToString() == "Enter") break;
    }
}
public void RequestStop(string word)
{
    _shouldStop = true;
    f =false;
    Console.WriteLine();
    Console.SetCursorPosition(44, 10);
    Console.WriteLine("- TIME OUT -");
    Console.SetCursorPosition(46, 12);
    if (sb.ToString() == word.ToUpper())
    {
        Console.WriteLine("CORRECT !");
        Console.SetCursorPosition(42, 13);
        Console.WriteLine("CONGRATULATIONS");
    }
    else { Console.SetCursorPosition(47, 12); Console.WriteLine("WRONG !"); }
    Console.SetCursorPosition(40, 15);
    Console.WriteLine("Press [Enter] to quit");
    Console.CursorVisible = false;
}

 private volatile bool _shouldStop;

}

public class WordPlay
{
  static void Main()
  {
    Console.SetBufferSize(100,50);
    Console.SetWindowSize(100,50);
    string[] words = { "angstrom", "abacinate", "abactinal", "abandoned", "Babesiidae", "babirussa", "Babylonian", "bacchantic", "cabassous", "cableway" };
    string word="";      
    Random randObj = new Random(0);
    Console.SetCursorPosition(40, 6);
    Console.WriteLine("Your String:");
    Console.WriteLine();       
    for (int j = 0; j < 20; j++)
    { 
       System.Threading.Thread.Sleep(150); Console.SetCursorPosition(53, 6); 
       Console.Write(words[randObj.Next(words.Length - 1)].ToUpper() + "     ");  
    }

    word = words[randObj.Next(words.Length - 1)].ToUpper();
    Console.SetCursorPosition(53, 6);
    Console.Write( word+"    ");
    Console.WriteLine();

    // Create the thread object. This does not start the thread.
    Worker workerObject = new Worker();
    Thread workerThread = new Thread(workerObject.DoWork);

    // Start the worker thread.
    workerThread.Start();

    // Loop until worker thread activates.
    while (!workerThread.IsAlive);

    // Put the main thread to sleep for 1 millisecond to
    // allow the worker thread to do some work:

    Thread.Sleep(3000);
    // Request that the worker thread stop itself:

    workerObject.RequestStop(word);

    // Use the Join method to block the current thread 
    // until the object's thread terminates.

    workerThread.Join();      
}

}

+2  A: 

Joe Albahari's free ebook on threading is an excellent introduction.

Mitch Wheat
There is world in it. It may take One week to thoroughly read the full article.
Nani
@Nani: maybe not a week, but if you want to gain a better understanding...
Mitch Wheat
+1  A: 

Also threading DOs and DON'Ts.

TheMachineCharmer
+2  A: 

Your main thread splits of exectly 1 thread and then waits for it to end. There is no purpose to the Thread except that it complicates things.

Threading Lesson #1:

Using Threads is not always an improvement.

Henk Holterman
you're right but I guess it has been done on a training purpose
PierrOz
@PierrOz: I know but it is very important to choose the right problem area for learning/training.
Henk Holterman
+3  A: 

This is not really a threading problem. It is more a Console problem.

If you press any key within a console, it will be buffered from the Console class and the ReadKey() function just gets the first character in that buffer.

Oliver
I think this is the only answer that actually addresses what's puzzling the OP.
Dan Tao
How to Solve this?
Nani
@Nani: I rarely use the console for my programs, so unfortunately i didn't came into that problem and so i never had to search for a solution.
Oliver
+1  A: 

Does it help to flush the console with Console.Out.Flush()? Your output from the main thread may be waiting in the output buffer while your second thread is already running.

Adrian Cox
+1  A: 

I'll use another probably more appropriate example for threading, through a BackgroundWorker component which is an easy-to-use multi-threading tool. Let's say I have a project with two windows, one is my application, the other is for use as a SplashScreen while the application loads. The BackgroundWorker component is great for such tasks.

using System.ComponentModel.Component;

private BackgroundWorker newThread = new BackgroundWorker();

public void appForm_Load(object sender, EventArgs e) {
    SplashForm sf = new SplashForm();
    sf.Parent = this;

    newThread.DoWork += new EventHandler(newThread_DoWork);
    newThread.RunWorkerAsync(); // Here starts the thread.

    sf.ShowDialog(); // Then shows your splash screen.
}

public void newThread_DoWork(object sender, DoWorkEventArgs e) {
    // TODO: Place the code required by yours tasks here, or call another method to do so.
}

This is a simple example. Assuming this would compile, it will perform your background tasks on a different thread while continuing to show you the splash screen. On this splash screen, you might wish to place a ProgressBar control so that your loading process's progress is reported to your SplashForm through the BackgroundWorker.ProgressChanged() method. You might also wish to Dispose() your SplashForm when the BackgroundWorker is done. This could be done through the BackgroundWorker.RunWorkerCompleted() method.

Following our example, you would have to take your SplashForm declaration outside of your appForm_Load() method so that the BackgroundWorker.RunWorkerCompleted() method may access it to disposal purposes, then your main form shall show.

This is according to me, humbly, the most efficient and easy way to do multi-threading. The BackgroundWorker is very programmer-friendly, without having to care a lot about the locking stuff, delegates, invocations and callbacks.

The .NET Framework 4 shall come with a lot of tools allowing multi-threading done easier: Threadsafe collections, parallel LINQ, etc.

Will Marcouiller
A: 

Hi Everybody, I got the solution. The actual problem is with ReadKey(); It doesn't read from the keyboard. Perhaps, it takes the buffer data.

Nani