Interacting between two threads
I am working on a winform application, and my goal is to make a label on my form visible to the user, and three seconds later make the label invisible. The issue here is timing out three seconds. I honestly do not know if this was the correct solution to my problem, but I was able to make this work by creating a new thread, and having the new thread Sleep for three seconds (System.Threading.Thread.Sleep(3000)).
I can't use System.Threading.Thread.Sleep(3000) because this freezes my GUI for 3 seconds!
private void someV开发者_Python百科oid()
{
lbl_authenticationProcess.Text = "Credentials have been verified authentic...";
Thread sleepThreadStart = new Thread(new ThreadStart(newThread_restProgram));
sleepThreadStart.Start();
// Once three seconds has passed / thread has finished: lbl_authenticationProcess.Visible = false;
}
private void newThread_restProgram()
{
System.Threading.Thread.Sleep(3000);
}
So, back to my original question. How can I determine (from my main thread) when the new thread has completed, meaning three seconds has passed?
I am open to new ideas as well as I'm sure there are many.
Right now, you are blocking the entire UI thread in order to hide a label after 3 seconds. If that's what you want, then just user Thread.Sleep(3000)
from within the form. If not, though, then you're best off using a Timer
:
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Interval = 3000;
timer.Tick += (s, e) => { this.lbl_authenticationProcess.Visible = false; timer.Stop(); }
timer.Start();
After 3 seconds, the label will disappear. While you're waiting for that, though, a user can still interact with your application.
Note that you must use the Forms version of Timer
, since its Tick
event is raised on the UI thread, allowing direct access to the control. Other timers can work, but interaction with the control would have to be Invoke/BeginInvoke
d.
Did you try to use Timer
System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
t.Interval = 3000;
t.Start();
t.Tick += new EventHandler(t_Tick);
void t_Tick(object sender, EventArgs e)
{
label.Visible = false;
}
You really don't need to synchronize anything. You just need a new thread, with a reference to your label. Your code is actually pretty close:
private void someVoid()
{
lbl_authenticationProcess.Text = "Credentials have been verified authentic...";
lbl_authenticationProcess.Visible = true;
Thread sleepThreadStart = new Thread(new ThreadStart(newThread_restProgram));
sleepThreadStart.Start();
}
private void newThread_restProgram()
{
System.Threading.Thread.Sleep(3000);
if (lbl_authenticationProcess.InvokeRequired) {
lbl_authenticationProcess.Invoke(new SimpleCallBack(makeInvisible));
} else {
makeInvisible();
}
}
private void makeInvisible()
{
lbl_authenticationProcess.Visible = false;
}
So, when someVoid() is called, the message on the label is set, the label is made visible. Then a new thread is started with the newThread_restProgram() as the body. The new thread will sleep for 3 seconds (allowing other parts of the program to run), then the sleep ends and the label is made invisible. The new thread ends automatically because it's body method returns.
You can make a method like so:
public void SetLbl(string txt)
{
Invoke((Action)(lbl_authenticationProcess.Text = txt));
}
And you would be able to call it from the second thread, but it invokes on the main thread.
If you're using .NET 3.5 or older, it's kinda a pain:
private void YourMethod()
{
someLabel.BeginInvoke(() =>
{
someLabel.Text = "Something Else";
Thread thread = new Thread(() =>
{
Thread.Sleep(3000);
someLabel.BeginInvoke(() => { someLabel.Visible = false; });
});
thread.Start();
});
}
That should stop you from blocking the UI.
If you're using .NET 4+:
Task.Factory.StartNew(() =>
{
someLabel.BeginInvoke(() => { someLabel.Text = "Something" });
}).ContinueWith(() =>
{
Thread.Sleep(3000);
someLabel.BeginInvoke(() => { someLabel.Visible = false; });
});
If you are willing to download the Async CTP then you could use this really elegant solution which requires the new async
and await
keywords.1
private void async YourButton_Click(object sender, EventArgs args)
{
// Do authentication stuff here.
lbl_authenticationProcess.Text = "Credentials have been verified authentic...";
await Task.Delay(3000); // TaskEx.Delay in CTP
lbl_authenticationProcess.Visible = false;
}
1Note that the Async CTP uses TaskEx
instead of Task
.
You can use an AutoResetEvent
for your thread synchronization. You set the event to signalled when your secondary thread has woken from it's sleep, so that it can notify your main thread.
That means though that your main thread waits for the other thread to complete.
On that note, you can use SecondThread.Join()
to wait for it to complete in your main thread.
You do either of the above, but you don't need to do both.
As suggested in the comments, having a UI thread sleep is not generally a good idea, as it causes unresponsiveness for the user.
However if you do that, you might as well just sleep your main thread and get rid of the extraneous need of the second thread.
I'm not exactly sure this is the right way to do it, but to answer your question, you have to use the Join() function.
public void CallingThread()
{
Thread t = new Thread(myWorkerThread);
t.Join();
}
public void WorkerThread()
{
//Do some stuff
}
You can also add a timeout as parameter to the function, but you don't need that here.
精彩评论