PDA

Click to See Complete Forum and Search --> : Threading help


pjrage
Jun 5th, 2007, 09:07 AM
I'm trying to learn more about threading by thinking of my own examples then trying them out.

I'm currently attempting to get something like a timer to work by using the Thread.Sleep method for the thread.

I want the timer to flash a label on my main form, but that is the problem. The reason I'm trying this instead of a timer is to have a different flash "on" time than "off" time, ie 500ms "on," 250ms "off" then repeat. This could be done with 2 timers on the main thread and would probably be OK but I'm just trying to learn threading.

To get the thread setup I have:

private Thread thread_flashy = null

in Form.Load..

thread_flashy = new Thread(new ThreadStart(Flashy));
thread_flashy.IsBackground = true;
thread_flashy.SetApartmentState(ApartmentState.STA);

in Button1.Click..

if (thread_flashy.IsAlive)
thread_flashy.Resume();
else
thread_flashy.Start();

private void Flashy()
{
while (true)
{
Thread.Sleep(500);
if (Label1.Visible)
{
Label1.Visible = false;
Thread.Sleep(250);
Label1.Visible = true;
}
}
}

The problem is that I cannot do any cross thread calls. I used method invoker so Flashy looked like:

private void Flashy()
{
while (true)
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(Flashy));
}
else
{
Thread.Sleep(500);
if (Label1.Visible)
{
Label1.Visible = false;
Thread.Sleep(250);
Label1.Visible = true;
}
}
}
}

This freezes up the program. I'm guessing because Invoke makes the particular function (Flashy) run on the main thread (since that is the thread which is calling Invoke)? And then, the "while (true)" locks it up. To support this theory, if I take out the "while (true)" block, it runs and blinks fine just once but will not repeat.

So what I'm wondering is... how can I get this to work? If I could add the label (Label1) somehow to the thread_flashy thread so that it is not a cross-thread call, that would work right? But could I still display it on the main form?

Maybe I'm going about this all wrong? Is there a simpler way? Another way to use a while (true) to loop, but that doesn't lockup the program? I guess I could throw an Application.DoEvents() in there but that seems like a not-the-right-way-at-all way to do this.

Also, can anyone confirm that using Invoke forces the function to run on a different thread? If this is the case, then doesn't using Invoke (for cross-thread calls) defeat the purpose of using a separate thread?

And sorry for being so ignorant about threading, I'm so new to it :eek2: :eek: This is probably a silly question overall :ehh:

JenniferBabe
Jun 5th, 2007, 01:40 PM
Let's say you're using a thread to update some values on a table in MainForm. If you try to update the table directly from the thread, the form will freeze since it's like 2 separate threads. Thus you will have to use the invoke to access the thread the MainForm is running on and then do your update.

Take a look at the following code:

public class clThread
{
private frmMain MAIN;

private Thread MainThread;

/* Delegate method to use with BeginInvoke method. */
private delegate void ChangeMainComponentDelegate();


public clThread(frmMain MAIN)
{
this.MAIN = MAIN;

MainThread = new Thread(new ThreadStart(Start)); // Instantiate the Thread.
MainThread.Start(); // Start the Thread.

}



public void Start()
{
// Instantiate values to for the table columns here. Probably a string or arraylist.


// The error I made was to add the code to update the table here. I took this
// out and added it to the method below which checks for being invoke.
// Call method to check if Invoke is required which will then update the Main Component Table.
ChangeMainComponent();

//
MainThread.Abort();


}// end start



private void ChangeMainComponent()
{
// Check if we need to call BeginInvoke.
if(MAIN.InvokeRequired)
{
/* Pass the same function to BeginInvoke, however this time the call will on the correct
* thread and InvokeRequired will be false. */
MAIN.BeginInvoke(new ChangeMainComponentDelegate(ChangeMainComponent), null);

return; // Return from this method, don't execute the lines below.

}// main invoke

// Code to actually update the table from the MAIN object which
// is frmMain form.

}



}// end clthread


Doing it this way didn't freeze the table, if I did not use the begininvoke, the table and form freezed. Hope this helps.

Jennifer

pjrage
Jun 5th, 2007, 03:06 PM
I don't fully understand all the code you put up, but I did find a way (that I think is the same thing essentially that you did) to make mine work.

Within my Flashy(), instead of checking .InvokeRequired and invoking Flashy again, like I did, I simply changed Flashy to:

private void Flashy()
{
while (true)
{
if (Label1.Visible)
{
Thread.Sleep(500);
Flashy_Off();
Thread.Sleep(250);
Flashy_On();
}
}
}

where Flashy_On and Off look like:

private void Flashy_On()
{
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(Flashy_On));
else
Label1.Visible = True;
}

private void Flashy_Off()
{
if(this.InvokeRequired)
this.Invoke(new MethodInvoker(Flashy_Off));
else
Label1.Visible = False;
}

This works just like I wanted. I'm not sure if it is "right," but it does what I wanted and doesn't appear to use any significant amount of the main thread's resources. That is, the .Sleep methods are definitely sleeping the proper thread now (thread_flashy) instead of the main thread which is what was locking up the program before.

I did what I did because I figured that using the Invoke method basically makes whatever thread calls Invoke (in my case the main thread) execute whatever you invoked. Since I was invoking Sleeps inside of an infinite while loop, the form froze. By invoking as little as possible (just the display portions) I was able to let the invoked thread (main thread) keep it's resources and force the Sleep methods (and in future cases, this could be any time consuming process) to occur on their own thread, which was my intention in the first place. I'm not sure if I did this right or wrong or even for the right or wrong reasons, but that is my reasoning and it seems to make sense (at least to me) and also seems to work :thumb: