I have this form that spawns a new thread and starts listening and waiting for UDP packets in a loop. What I need is to keep the UI updated with the number of bytes received.
For that, I have setup an event which I'll raise as soon as a packet is received and pass the number of bytes received as an argument. Since I'm not running on the UI thread, I cannot simply update the UI directly. Here's what I'm currently doing:
private void EVENTHANDLER_UpdateTransferProgress(long receivedBytes) {
if(InvokeRequired) {
Invoke(new MethodInvoker(() => {
totalReceivedBytes += receivedBytes;
Label.Text = totalReceivedBytes.ToString("##,0");
}));
}
}
But this is still running on the same thread as the packet reception loop and it will not return to that loop - and wait for another packet - until this EVENTHANDLER_UpdateTransferProgress method returns.
My question is basically about the following line in the method above:
Label.Text = totalReceivedBytes.ToString("##,0");
Updating the UI like this slows down the packet reception. If I take that line off (or comment it), the packet reception will be much faster.
How can I possibly solve this issue? I think more threads is the key, but I'm not sure how to properly implement them in this situation... I'm using Windows Forms with .NET 2.0.
EDIT:
On my previous testing, the above seem to be true and it may actually be to some extent. But after a little more testing I realized the problem was on the whole Invoke(new MethodInvoker(() => { ... })); thing. When I remove that (the UI won't be updated of course) and leave EVENTHANDLER_UpdateTransferProgress but keep raising the event, the packet reception is much faster.
I tested receiving some file which took in average about ~1.5sec without calling Invoke() at all on the event handler. When I did call Invoke() in the event handler, even without updating any control in the UI or doing any operation (in other words, the anonymous method body was empty), it took much longer, around ~5.5sec. You can see it's a big difference.
Is there anyway to improve this?
The problem with your approach is that it updates the UI on every single packet. If you received 1000 packets every second, you would update the UI 1000 times every second! The monitor probably doesn't refresh more than 100 times per second, and nobody is going to be able to read it if it updates more than 10 times per second.
A better way to approach this problem is to put the totalReceivedBytes += receivedBytes; in the thread that handles the I/O and put a timer on the UI thread that executes Label.Text = totalReceivedBytes.ToString("##,0"); only a few times per second at most. When the transfer starts, start the timer; when the transfer stops, stop the timer.
Yes, there is a way to improve this.
The first is to use BeginInvoke instead of Invoke which will not wait for the invoke to return. You should also consider using another form in your method
private void EVENTHANDLER_UpdateTransferProgress(long receivedBytes) {
if(InvokeRequired) {
BeginInvoke(new Action<long>(EVENTHANDLER_UpdateTransferProgress),
receivedBytes));
return;
}
totalReceivedBytes += receivedBytes;
Label.Text = totalReceivedBytes.ToString("##,0");
}
So if you call this method from a method that does not require invoking, the update on the GUI is still performed.
Another option that you can do is break of a thread in your download thread. Something in the likes of
public event EventHandler<MonitorEventArgs> ReportProgress;
public void startSendingUpdates(MonitorEventArgs args) {
EventHandler<MonitorEventArgs> handler = ReportProgress;
if (handler == null) {
return;
}
ThreadPool.QueueUserWorkItem(delegate {
while (!args.Complete) {
handler(this, args);
Thread.Sleep(800);
}
});
}
public void download() {
MonitorEventArgs args = new MonitorEventArgs();
startSendingUpdates(args);
while (downloading) {
int read = downloadData(bytes);
args.BytesTransferred += read;
}
args.Complete = true;
}
public class MonitorEventArgs : EventArgs {
public bool Complete { get; set; }
public long BytesTransferred { get; set; }
}
The overhead of this is kind of small compared to the benefits. Your download thread is not affected by the updates to the GUI (at least not compared to waiting on the GUI to update). The downside is you are occupying a thread in the threadpool, but hey, that's what they're there for! And, the thread shuts down when it's done, since you set the complete flag. You don't need to lock when setting that either, since an extra run in the worker thread is unimportant in the context.
Have you tried using BeginInvoke instead of Invoke? BeginInvoke() is an asychronous call.
private void EVENTHANDLER_UpdateTransferProgress(long receivedBytes) {
if(InvokeRequired) {
BeginInvoke(new MethodInvoker(() => {
totalReceivedBytes += receivedBytes;
Label.Text = totalReceivedBytes.ToString("##,0");
}));
}
}
Related
I'm trying to use a background worker to update a listbox used for a status window in my Form in C#. It doesn't appear to work properly when the addToStausLog() method is called from another class outside of the MyForm class even though I pass an instance of the form to the other class that's calling the addToStatusLog update member. Instead the update doesn't happen until the class member finished and returns back to the MyForm class. Maybe there's a better a approach to creating real-time status windows that will run from any class that MyForm is passed into. I'm new to worker threads, so could someone review and let me know what I might be doing wrong or could improve on.
public MyForm()
{
InitializeComponent();
// Setup background task to update listbox status so UI is unaffected
_lListBoxQue = new List<string>();
bw_listBoxBGWorker = new BackgroundWorker();
bw_listBoxBGWorker.DoWork += (o, args) => LstbxThread_doWork();
bw_listBoxBGWorker.RunWorkerCompleted += (o, args) => LstbxThread_completed();
}
private void LstbxThread_doWork()
{
System.Threading.Thread.Sleep(100);
}
private void LstbxThread_completed()
{
// Update listbox
lstStatusBox.BeginUpdate();
lstStatusBox.Items.Clear(); // clear entries
lstStatusBox.Items.AddRange(_lListBoxQue.ToArray());
lstStatusBox.EndUpdate();
}
public String addToStatusLog(String sMsg)
{
_lListBoxQue.Add(sMsg);
if (_lListBoxQue.Count > _iStatusLogMaxLines) // > max?
_lListBoxQue.RemoveAt(0); // remove top element?
if( !bw_listBoxBGWorker.IsBusy ) // background not busy?
bw_listBoxBGWorker.RunWorkerAsync(); // update listbox in back ground task
System.Threading.Thread.Sleep(100);
return sMsg;
}
This is the member that calls another class which attempts to call the addToStatusLog several times during the process, but the updates to the listbox don't happen until the MyClass(this).updateDB() finishes. I need to see real-time updates as the updateDB() function is running. There has to be a way to make this work, I'm hoping...
private void btnUpdateDB_Click(object sender, EventArgs e)
{
if (_bIsUpdateEventRunning == false ) // is event not busy?
{
_bIsUpdateEventRunning = true;
new MyClass(this).updateDB();
_bIsUpdateEventRunning = false;
}
}
Example of class called to update the form listbox.
Public class MyClass{
private MyForm _pForm;
public MyClass(MyForm pForm){ _pForm= pForm; }
public void updateDB(){
_pForm.addToStatusLog("Hello World");
}
}
Updated Fix w/o background worker:
public String addToStatusLog(String sMsg)
{
_lListBoxQue.Add(sMsg);
if (_lListBoxQue.Count > _iStatusLogMaxLines) // > max?
_lListBoxQue.RemoveAt(0); // remove top element?
lstStatusBox.BeginUpdate();
lstStatusBox.Items.Clear(); // clear entries
lstStatusBox.Items.AddRange(_lListBoxQue.ToArray());
lstStatusBox.EndUpdate();
Application.DoEvents();
return sMsg;
}
Thread.Sleep is not the answer here. What you likely need is Application.DoEvents. This processes all messages currently waiting in the Windows message queue.
Thread.Sleep just tells the thread to go to sleep for the number of milliseconds you specify. If your background worker is running on the UI thread, you're putting the UI thread to sleep and it's effectively comatose. (Important: All Windows forms run on the UI thread.)
There are, of course, alternative designs that involve spinning up separate threads of execution. But these have their own issues, and you should be mindful of them before running blindly down that path.
I have mainly been reusing a code snippet from old times:
public void Start()
{
renewalThread = new Thread(() =>
{
while (!disposed)
{
Thread.Sleep(TimeSpan.FromSeconds(10));
try
{
if (LogUpdated != null)
update();
}
catch (Exception ex)
{
}
}
});
renewalThread.Start();
}
Are there more elegant ways to do this, thinking about the new async/await stuff?
What are the main differences to a solution doing something like
Task.run( () =>
{
await Task.delay(10000);
update code
}, __.LongRunning);
Use a Timer instead:
aTimer = new System.Timers.Timer(10000);
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Enabled = true;
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
// do something here.
// if this method could take longer than the intervale, disable the
// timer at the start and re-enable at the end.
}
With Timer you don't have to start a new thread. Thread.Sleep forces you to use a thread that sits and waits. If you want to do something every x seconds, that's what System.Threading.Timer is designed for, it will take a thread-pool thread and use that when calling the event and the thread will only be in use during the event--unlike Sleep. Sleep is inaccurate--it could be less than the time you asked for or more. the likelihood of it being that much off with 10 seconds is nil; but it's sill inaccurate. Using thread.Sleep means you can't do two events at once--if your Timer event handler took more time than the interval, it would run two handlers at a time. A Timer is much easier to stop--you just call Stop or Dispose. With Thread.Sleep you have to use Thread.Abort--and risk data corruption (i.e. you have to write the code that calls Thread.Sleep in such a way that cancelling the thread doesn't corrupt data). If you need to do something on the UI thread in the event, use Forms.Timer and you don't have to deal with marshalling back to the UI thread (e.g. Control.BeginInvoke).
I could go on, but I think you get the point. For more details, see http://bit.ly/IhxHSk
I have a class to launch background operations in a WinForms application. I need to write this background worker since my requisites are using .NET 1.1, so I cannot use BackgroundWorker, that is only available from .NET 2.0
This class get a delegate and execute it in a thread. I want the main thread to respond to events.
I also want to indicate that the operation is running setting the application cursor to Cursors.WaitCursor.
What do you think about current implementation? I'm interested in the method WaitTillThreadFinishes(), because I'm not sure about Application.DoEvents(), please read the code and share with me opinions about WaitTillThreadFinishes.
The following code executes the operation:
private object ExecuteOperation (Delegate target, params object[] parameters)
{
mTargetDelegate = target;
mTargetParameters = parameters;
mTargetThread = new Thread(new ThreadStart(ThreadProc));
mTargetThread.Name = mTargetDelegate.Method.Name;
mOperationFinished = false;
// start threaded operation
mTargetThread.Start();
// perform active waiting
WaitTillThreadFinishes();
return mTargetResult;
}
The following code is executed in a thread, simply call the delegate, and wrap exceptions:
protected virtual void ThreadProc()
{
try
{
mTargetResult = mTargetDelegate.DynamicInvoke(mTargetParameters);
}
catch (ThreadAbortException) { }
catch (Exception ex)
{
//manage exceptions here ...
}
finally
{
mOperationFinished = true;
}
}
And this is the code performs an active waiting. I'm interested on share with you. Any better option? Any pain calling Application.DoEvents() massively?
private void WaitTillThreadFinishes ()
{
// Active wait to respond to events with a WaitCursor
while (!mOperationFinished)
{
// sleep to avoid CPU usage
System.Threading.Thread.Sleep(100);
Application.DoEvents();
Cursor.Current = Cursors.WaitCursor;
}
Cursor.Current = Cursors.Default;
}
Thanks in advance.
Please let me know if i understood your question correctly.
Why dont you use an event to notify the UI that the worker finished his job?
This way, the UI doen't get blocked by the worker, and you avoid busy waiting.
Sample Implementation
public class MyBackgroundWorker
{
// Fields
private Delegate _target;
private object[] _arguments;
// Events
public event EventHandler RunWorkerStarted;
public event EventHandler<RunWorkerCompletedEventArgs> RunWorkerCompleted;
// Event Invocators
public void InvokeRunWorkerStarted()
{
var handler = RunWorkerStarted;
if (handler != null) handler(this, new EventArgs());
}
public void InvokeRunWorkerCompleted(object result)
{
var handler = RunWorkerCompleted;
if (handler != null) handler(this, new RunWorkerCompletedEventArgs(result));
}
public void RunWorkerAsync(Delegate target, params object[] arguments)
{
_target = target;
_arguments = arguments;
new Thread(DoWork).Start(arguments);
}
// Helper method to run the target delegate
private void DoWork(object obj)
{
_target.DynamicInvoke(_arguments);
// Retrieve the target delegate's result and invoke the RunWorkerCompleted event with it (for simplicity, I'm sending null)
InvokeRunWorkerCompleted(null);
}
}
internal class RunWorkerCompletedEventArgs : EventArgs
{
public RunWorkerCompletedEventArgs(object result)
{
Result = result;
}
public object Result { get; set; }
}
Usage
In the UI you can use it this way:
private void button1_Click(object sender, EventArgs e)
{
var worker = new MyBackgroundWorker();
worker.RunWorkerStarted += worker_RunWorkerStarted;
worker.RunWorkerCompleted += worker_Completed;
worker.RunWorkerAsync(new MethodInvoker(SomeLengthyOperation), null);
}
void worker_RunWorkerStarted(object sender, EventArgs e)
{
}
void worker_Completed(object sender, EventArgs e)
{
MessageBox.Show("Worker completed");
}
private void SomeLengthyOperation()
{
Thread.Sleep(5000);
}
Final Notes
Remember to Invoke() in the event handlers to access the UI thread correctly. You can also modify the worker so this is done in a safe way.
There isn't much support in 1.1 for doing this, but I'll tell you what I'd do (sorry, no code at this time).
As for the asynchronous operation, I'd use the APM to kick off and complete the asynchronous method. This is fully supported in 1.1, so no worries there.
The idea is that in the UI, you store some indication that work is being done (a boolean field, for example) and (optionally) a Timer used to "wake up" the UI on a regular basis to check on the current status of the background work and indicate this to the user.
You would set the boolean to indicate you are working in the background, call BeginInvoke() on your delegate (using the overload that takes a callback search for "Executing a Callback Method When an Asynchronous Call Completes
"), and start the Timer. When the user attempts to use the UI, you would optionally check the boolean and cancel the operation, thus preventing the user from doing something harmful while you are waiting. When the timer Ticks, you can check the status of your asynchronous method by, say, a shared field that the method writes updates to and the UI reads. For example, a double which the UI uses to update a progress bar.
Once the callback fires, you clean up your asynchronous mess (i.e., call EndInvoke, and handle any exceptions thrown, etc), turn off the Timer and reset your boolean running indication field.
By using this method, you can keep the UI completely responsive (and partially usable, depending on your overall design), can set up a mechanism to abort the background worker (through the use of another field, the reverse of the boolean mentioned earlier, and inform the user of the status of the operation.
There is occasionally a case for kicking off a thread and waiting for its return, if you are doing other things in the meantime, but in this case, with the code you have shown, it is meaningless.
If you want the threadProc to allow for events to be processed, then call doevents in that, which will free up the CPU briefly, allowing for processing.
Unless you have a particular reason for needing to thread processes, you should not do it. Getting it right - as Ian Boyd has said - is difficult, and the more you need to interact with it the harder it is. If you can run fire-and-forget threads, that is the easiest.
Ideally you start the asynchronous operation and leave your form alone (aside from maybe using the Cursors.AppStarting cursor).
When your threaded operation completes, it then needs to fire some sort of BackgroundOperationComplete event. This is where your would call from your asynchronous delegate code:
form.Invoke(BackgroundOperationComplete);
The form's BackgroundOperationComplete method is where you can handle the fact that the background operation is complete:
void BackgroundOperationComplete()
{
this.Cursor = Cursors.DefaultCursor;
lblAnswer.Text = "The thread is done";
}
If all else fails, keep the operation synchronous, and use an IProgressDialog. (brief conceptual pseudo-code from memory):
void DoStuff()
{
IProgressDialog pd = new ProgressDialog();
pd.SetTitle = "Calculating Widgets";
pd.StartTimer(PDTIMER_RESET, NULL)
pd.StartProgressDialog(this.Handle, NULL, PROGDLG_MODAL | PROGDLG_NOTIME | PROGDLG_NOPROGRESSBAR | PROGDLG_NOCANCEL, NULL);
try
{
pd.SetLine(1, "Please wait while the widgets are frobbed");
DoTheThingThatDoesTheSynchronousStuff();
}
finally
{
pd.StopProgressDialog();
}
pd = null;
}
In my code I subscribe to an event that happens on a different thread. Every time this event happens, I receive an string that is posted to the observable collection:
Dispatcher currentDispatcher = Dispatcher.CurrentDispatcher;
var SerialLog = new ObservableCollection<string>();
private void hitStation_RawCommandSent(object sender, StringEventArgs e)
{
string command = e.Value.Replace("\r\n", "");
Action dispatchAction = () => SerialLog.Add(command);
currentDispatcher.BeginInvoke(dispatchAction, DispatcherPriority.Render);
}
The code below is in my view model (could be in the code behind, it doesn't matter in this case). When I call "hitstation.PrepareHit", the event above gets called a couple times, then I wait and call "hitStation.HitBall", and the event above gets called a couple more times.
private void HitBall()
{
try
{
try
{
Mouse.OverrideCursor = Cursors.Wait;
//prepare hit
hitStation.PrepareHit(hitSpeed);
Thread.Wait(1000);
PlayWarning();
//hit
hitStation.HitBall(hitSpeed);
}
catch (TimeoutException ex)
{
MessageBox.Show("Timeout hitting ball: " + ex.Message);
}
}
finally
{
Mouse.OverrideCursor = null;
}
}
The problem I'm having is that the ListBox that is bound to my SerialLog gets updated only when the HitBall method finishes. I was expecting seeing a bunch of updates from the PrepareHit, a pause and then a bunch more updates from the HitBall.
I've tried a couple of DispatcherPriority arguments, but they don't seem to have any effect.
I think you are blocking yourself.
The UI Thread is waiting at Thread.Wait, BeginInvoke sends the action to the dipatcher, however the UI Thread is busy waiting. That is why UI updates only get done after HitBall finishes, that is, when the UI Thread finishes processing.
To get around this, you should start the HitBall method's code in another thread, freeing the UI:
private void HitBall()
{
try {
Mouse.OverrideCursor = Cursors.Wait;
Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
Action hitOperations = () =>
{
hitStation.PrepareHit(hitSpeed);
Thread.Wait(1000);
//Only needed if PlayWarning needs access to UI Thread
dispatcher.Invoke(() => PlayWarning());
hitStation.HitBall(hitSpeed);
dispatcher.Invoke(() => Mouse.OverrideCursor = null);
};
//Free the UI from work, start operations in a background thread
hitOperations.BeginInvoke(null, null);
}
catch (TimeoutException ex)
{
MessageBox.Show("Timeout hitting ball: " + ex.Message);
}
}
Also if the intended use of Thread.Wait(1000) was to wait for events to refresh the UI, with this implementation it is no longer needed.
Could it be something as simple as needing to raise the PropertyChanged event on your ViewModel?
You will probably not see updates from PrepareHit unless there is a context switch and there is no guarantee when context switch will occur (unless you block your current thread and that would increase the probability that a context switch will occur).
As iCe mentioned if you're doing this on the UI thread, then you might be blocking your UI. Does your UI block/freeze when you call Thread.Wait? If it doesn't then proceed reading below:
Update:
I can't think of anything that would not compromise concurrency... without compromising concurrency you could try to increase the priority of BeginInvoke: http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx
currentDispatcher.BeginInvoke(dispatchAction, DispatcherPriority.Send);
By the way, it looks like Render priority is lower than Normal priority:
Render The enumeration value is 7.
Operations processed at the same
priority as rendering.
DataBind The
enumeration value is 8. Operations are
processed at the same priority as data
binding.
Normal The enumeration value
is 9. Operations are processed at
normal priority. This is the typical
application priority.
Send The
enumeration value is 10. Operations
are processed before other
asynchronous operations. This is the
highest priority.
I'm working on a card game in C# for a project on my Intro to OOP paper and have got the game working now but am adding "flair" to the GUI.
Currently cards are dealt and appear on the UI instantaneously. I want to have to program pause for a moment after dealing a card before it deals the next.
When a game is started the following code runs to populate the PictureBoxes that represent them (will be a loop eventually):
cardImage1.Image = playDeck.deal().show();
cardImage2.Image = playDeck.deal().show();
cardImage3.Image = playDeck.deal().show();
cardImage4.Image = playDeck.deal().show();
cardImage5.Image = playDeck.deal().show();
...
I have tries using System.Threading.Thread.Sleep(100); between each deal().show() and also inside each of those methods but all it achieves is locking up my GUI until all of the sleeps have processed then display all of the cards at once.
I have also tried using a combination of a timer and while loop but it resulted in the same effect.
What would be the best way of achieving the desired result?
The problem is that any code that you run on the UI will block the UI and freeze the program. When your code is running (even if it's running Thread.Sleep), messages (such as Paint or Click) sent to the UI will not be processed (until control returns to the message loop when you exit your event handler), causing it to freeze.
The best way to do this is to run on a background thread, and then Invoke to the UI thread between sleeps, like this:
//From the UI thread,
ThreadPool.QueueUserWorkItem(delegate {
//This code runs on a backround thread.
//It will not block the UI.
//However, you can't manipulate the UI from here.
//Instead, call Invoke.
Invoke(new Action(delegate { cardImage1.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
Invoke(new Action(delegate { cardImage2.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
Invoke(new Action(delegate { cardImage3.Image = playDeck.deal().show(); }));
Thread.Sleep(100);
//etc...
});
//The UI thread will continue while the delegate runs in the background.
Alternatively, you could make a timer and show each image in the next timer tick. If you use a timer, all you should do at the beginning is start the timer; don't wait for it or you'll introduce the same problem.
Normally I'd simply recommend a function like this to perform a pause while allowing the UI to be interactive.
private void InteractivePause(TimeSpan length)
{
DateTime start = DateTime.Now;
TimeSpan restTime = new TimeSpan(200000); // 20 milliseconds
while(true)
{
System.Windows.Forms.Application.DoEvents();
TimeSpan remainingTime = start.Add(length).Subtract(DateTime.Now);
if (remainingTime > restTime)
{
System.Diagnostics.Debug.WriteLine(string.Format("1: {0}", remainingTime));
// Wait an insignificant amount of time so that the
// CPU usage doesn't hit the roof while we wait.
System.Threading.Thread.Sleep(restTime);
}
else
{
System.Diagnostics.Debug.WriteLine(string.Format("2: {0}", remainingTime));
if (remainingTime.Ticks > 0)
System.Threading.Thread.Sleep(remainingTime);
break;
}
}
}
But there seems to be some complication in using such a solution when it is called from within an event handler such as a button click. I think the system wants the button click event handler to return before it will continue processing other events because if I try to click again while the event handler is still running, the button depresses again even though I'm trying to drag the form and not click on the button.
So here's my alternative. Add a timer to the form and create a dealer class to handle dealing with cards by interacting with that timer. Set the Interval property of the timer to match the interval at which you want cards to be dealt. Here's my sample code.
public partial class Form1 : Form
{
CardDealer dealer;
public Form1()
{
InitializeComponent();
dealer = new CardDealer(timer1);
}
private void button1_Click(object sender, EventArgs e)
{
dealer.QueueCard(img1, cardImage1);
dealer.QueueCard(img2, cardImage2);
dealer.QueueCard(img3, cardImage1);
}
}
class CardDealer
{
// A queue of pairs in which the first value represents
// the slot where the card will go, and the second is
// a reference to the image that will appear there.
Queue<KeyValuePair<Label, Image>> cardsToDeal;
System.Windows.Forms.Timer dealTimer;
public CardDealer(System.Windows.Forms.Timer dealTimer)
{
cardsToDeal = new Queue<KeyValuePair<Label, Image>>();
dealTimer.Tick += new EventHandler(dealTimer_Tick);
this.dealTimer = dealTimer;
}
void dealTimer_Tick(object sender, EventArgs e)
{
KeyValuePair<Label, Image> cardInfo = cardInfo = cardsToDeal.Dequeue();
cardInfo.Key.Image = cardInfo.Value;
if (cardsToDeal.Count <= 0)
dealTimer.Enabled = false;
}
public void QueueCard(Label slot, Image card)
{
cardsToDeal.Enqueue(new KeyValuePair<Label, Image>(slot, card));
dealTimer.Enabled = true;
}
}
The cheap way out would be to loop with calls to Application.DoEvents() but a better alternative would be to set a System.Windows.Forms.Timer which you would stop after the first time it elapses. In either case you'll need some indicator to tell your UI event handlers to ignore input. You could even just use the timer.Enabled property for this purpose if it's simple enough.
I would try puting the code that deals the deck ( and calls Thread.Sleep) in another thread.