C# Threads 101 question

Started by
6 comments, last by joanusdmentia 19 years, 1 month ago
For my textbased/window game, I have a command called "/sleep". This asks the player how many hours he would like to sleep, and he can enter in an amount. After each hour, it should output to the output screen (txtOutput) saying "1 hour has passed," etc. Right now, there's a long wait til it outputs anything, and when it does, it outputs more than one message. Obviously, this is because I'm running this under one thread. I need a second thread to make this work, I believe. Since I'm new to threads, I'm hoping you can tell me what I'm doing wrong here. After player types in /sleep, bHoursToSleep's boolean state is turned to true. Next, the program will notice bHoursToSleep is true, so it should do the following game state:

               // get boolean states
                if (bHoursToSleep == true)
                {
                    hours = int.Parse(sBuffer);

                    ThreadStart hoursThread = new ThreadStart(sleepingTime);
                    Thread sleepThread = new Thread(hoursThread);
                    sleepThread.Name = "Sleeping Thread";
                    sleepThread.Start();
                }

ThreadStart calls the sleepingTime() method.

        public void sleepingTime()
        {
            try
            {                
                for (int i = 0; i < hours; i++)
                {
                    AddText(i + " hour(s) pass by."); 
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }

            bHoursToSleep = false;  // turns off boolean state
        }

Error during runtime, pointing at: Application.Run(new frmMain());

Illegal cross-thread operation: Control 'txtOutput' accessed from a thread
 other than the thread it was created on.\r\nStack trace where the illegal operation occurred was:\r\n\r\n\tat System.Windows.Forms.Control.get_Handle()
\r\n\tat System.Windows.Forms.RichTextBox.ForceHandleCreate()\r\n\tat 
System.Windows.Forms.RichTextBox.get_Text()\r\n\tat 
The_Nral_Prophecies.frmMain.AddText(String)\r\n\tat The_Nral_Prophecies.frmMain.sleepingTime()\r\n\tat 
System.Threading._Thread.ThreadStart_Context(Object)\r\n\tat 
System.Threading.ExecutionContext.Run(ExecutionContext, ContextCallback, 
Object, StackCrawlMark&)\r\n\tat System.Threading._Thread.ThreadStart()\r\n\"
I tried making this clear as much as possible. Please let me know with some code examples too since I seem to better understand them. Thanks in advanced. Phil
Advertisement
Phil, there are times for threads and there are times not for threads. This is a time not for threads. (hope your going well, btw).

If you have controll over the game clock, then maybe something like

gameclock += houramountprint "Sleeping..."for x = 0 to houramountprint "Slept for " x " hours"wait(100)next x


Maybe?

Also, try not to do this with your parsing.

Entered "\sleep"

You do and uppercase it to "\SLEEP", you can then do a function pointer (or similar, there are functions like "callbyname" which work fine in vb6) and call the sleep function, with the rest of the paramiters (freshly parsed, by a different function).

You do not go

Entered "\sleep"

Go and set up bool flags, and a whole lot of other stuff
Check bool flags, and do something

It will just make things harder later on in the project.

From,
Nice coder
Click here to patch the mozilla IDN exploit, or click Here then type in Network.enableidn and set its value to false. Restart the browser for the patches to work.
You don't want threading to do this (at least, not directly). Check out the System.Timers.Timer class.
"Voilà! In view, a humble vaudevillian veteran, cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valorous visitation of a bygone vexation stands vivified, and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The only verdict is vengeance; a vendetta held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous. Verily, this vichyssoise of verbiage veers most verbose, so let me simply add that it's my very good honor to meet you and you may call me V.".....V
Quote:Original post by Nice Coder
gameclock += houramountprint "Sleeping..."for x = 0 to houramountprint "Slept for " x " hours"wait(100)next x

I wouldn't recommend doing this, it prevents you from doing anything else during the wait() call (at least, nothing in the current thread).
"Voilà! In view, a humble vaudevillian veteran, cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valorous visitation of a bygone vexation stands vivified, and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The only verdict is vengeance; a vendetta held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous. Verily, this vichyssoise of verbiage veers most verbose, so let me simply add that it's my very good honor to meet you and you may call me V.".....V
I think I see what you're saying. I rearranged my code to this...

                // get boolean states                if (bHoursToSleep == true)                {                    try                    {                        int hours = int.Parse(sBuffer);                        for (int i = 0; i < hours; i++)                        {                            AddText(i + " hour(s) pass by.");                            Thread.Sleep(300);                        }                        bHoursToSleep = false;                    }                    catch (Exception e)                    {                        MessageBox.Show(e.Message);                    }                }


However, using the for(;;) is causing the program to wait til its fully done until it outputs anything to the screen. That's why I thought threads would prevent that from happening, and print each one out individually. Please note that this is in Windows and not in console. I have no problem doing this in the console, but it seems like it's different for Windows. Please let me know, and thanks joanusdmentia for the link.
Currently, everything freezes when I start the sleep command. Here's my code once again..

                // get boolean states                if (bHoursToSleep == true)                {                                     try                    {                          hours = int.Parse(sBuffer);                        System.Timers.Timer aTimer = new System.Timers.Timer();                        aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);                        // Set the Interval to 5 seconds.                        aTimer.Interval = 1000;                        aTimer.Enabled = true;                        while (i != hours) ;                        bHoursToSleep = false;                    }                    catch (Exception e)                    {                        MessageBox.Show(e.Message);                    }                }


        public void OnTimedEvent(object source, ElapsedEventArgs e)        {            AddText(i + " hour(s) pass by.");            i++;        }


[Edited by - phil05 on March 3, 2005 5:19:57 AM]
It's been 7 hours, but the question still remains.
You don't want that while() statement in your Sleep() function, you're preventing the windows message loop from executing so nothing will get updated.

First, a class that handles sleeping
class SleepTimer{    private event EventHandler hourElapsed;    private event EventHandler finishedSleeping;    private int hoursToSleep;    private int hoursPassed;    private System.Timers.Timer timer;    public SleepTimer()    {        timer = new System.Timers.Timer();        timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);        timer.Interval = 1000;    }    public void StartSleeping(int hours)    {        hoursToSleep = hours;        hoursPassed = 0;        timer.Enabled = true;    }    public boolean IsSleeping    {        get { return hoursPassed<hoursToSleep; }    }    public event EventHandler HourElapsed    {        add { hourElapsed += value; }        remove { hourElapsed -= value; }    }    public event EventHandler FinishedSleeping    {        add { finishedSleeping += value; }        remove { finishedSleeping -= value; }    }    private void OnTimedEvent(object source, ElapsedEventArgs e)    {        if(hourElapsed!=null) hourElapsed();        if (++hoursPassed == hoursToSleep) {            timer.Enabled = false;            if(finishedSleeping!=null) finishedSleeping();        }    }}


You would set the sleep timer up at initialisation
    sleepTimer = new SleepTimer();    sleepTimer.HourElapsed += new EventHandler(HourElapsedHandler);    sleepTimer.FinishSleeping += new EventHandler(FinishSleepingHandler);


And to start sleeping, it's a one-liner
    sleepTimer.StartSleeping(hours);


Finally, in your game loop just check if we're sleeping
while(!quit){    // Run the windows message loop    Application.DoEvents();    // Update stuff here    //...    // Only handle user input if we aren't sleeping    if(!sleepTimer.Sleeping)        HandlerUserInput();}


[Edited by - joanusdmentia on March 3, 2005 2:25:44 PM]
"Voilà! In view, a humble vaudevillian veteran, cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valorous visitation of a bygone vexation stands vivified, and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The only verdict is vengeance; a vendetta held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous. Verily, this vichyssoise of verbiage veers most verbose, so let me simply add that it's my very good honor to meet you and you may call me V.".....V

This topic is closed to new replies.

Advertisement