Pause Thread While Another Thread Is Executing A Task - c#

I created a Thread that executes a task, but i need to pause my Main thread until my secondary threads ends the task.
private void AquilesPL_Load(object sender, EventArgs e)
{
ThreadStart ts = new ThreadStart(RunTask)
Thread t = new Thread(ts);
t.Start();
SomeFunction1();
SomeFunction2();
//I need to pause the main thread here, if runtask() continue working
//if runt task ends, this main thread must to continue.
ReadFile();
CloseProgram();
}
private void RunTask()
{
//Some code that write a file
//RunTaskfunction ends, and i have to continue
}
private void ReadFile()
{
//Reading the file, this file has been written by RunTask
}
Thanks in advance.

but i need to pause my Main thread until my secondary threads ends the task.
This is typically a bad idea. A better solution is to disable the UI while the task is executing, then re-enable it when it's completed.
The TPL and async/await make this fairly straightforward. For example:
private async void AquilesPL_Load(object sender, EventArgs e)
{
var task = Task.Run(() => RunTask());
SomeFunction1();
SomeFunction2();
// Disable your UI controls
await task; // This will wait until the task completes,
// but do it asynchronously so it does not block the UI thread
// This won't read until the other task is done
ReadFile();
// Enable your UI controls here
}
If you can't use C# 5, you can do this via .NET 4 and the TPL:
private void AquilesPL_Load(object sender, EventArgs e)
{
var task = Task.Factory.StartNew(() => RunTask());
SomeFunction1();
SomeFunction2();
// Disable your UI controls
task.ContinueWith(t =>
{
// This won't read until the other task is done
ReadFile();
// Enable your UI controls here
}, TaskScheduler.FromCurrentSynchronizationContext());
}

Related

How to close WPF window inside of a Task

I need to run a task to check the connection.
My windows should not be frozen during this check.
So I start the Task, and close my window at the end of this task.
But this returns an exception: InvalidOperationException:'The calling thread cannot access this object because another thread owns it'.
Like this :
private void Window_ContentRendered(object sender, System.EventArgs e)
{
Task.Run(() =>
{
ConnectionState = false;
if (NetworkTools.CheckGlobalConnection() == (ConnectionStatus.NetworkConnectionSuccess, ConnectionStatus.ServerConnectionSuccess))
{
ConnectionState = true;
}
this.Close();
});
}
How do I close my window at the end of the task without freezing it and without having this exception ?
(I have a progress bar that rotates indefinitely)
Or you could just use async await. Task.Run will offload and the await will create a continuation on current SynchronizationContext. In turn giving control back to the UI and closing on the right thread.
private async void Window_ContentRendered(object sender, System.EventArgs e)
{
await Task.Run(() =>
{
ConnectionState = false;
if (NetworkTools.CheckGlobalConnection() == (ConnectionStatus.NetworkConnectionSuccess, ConnectionStatus.ServerConnectionSuccess))
ConnectionState = true;
});
this.Close();
}
Also as noted, Calling ConfigureAwait(false), would not be the right thing to do in this case
Use Dispatcher to queue window closing logic on the unique UI thread.
Something like
Dispatcher.Invoke(
() =>
{
// close window here
});
Whatever is passed into .Invoke(...) as a delegate, is invoked on the UI thread and hence has right to access all UI elements. It is common (and the only correct) way to deal with UI-mutations within non-UI threads.
As an alternate method you can use ContinueWith
private void Window_ContentRendered(object sender, System.EventArgs e)
{
Task.Run(() =>
{
// Your code
}).ContinueWith((tsk) =>
{
this.Close();
}, TaskScheduler.FromCurrentSynchronizationContext());
}

keep windows.forms controls in same Main thread

So, I have a win form that calls a method
dvm.SetVoltage(excitationVoltage, rigNo);
which runs a task in another class
public void SetVoltage(double voltage, int rigNum)
{
Task.Run(() => ReadDVMWorker());
}
Once the worker is finished (voltage set) it triggers an event In the main Form1.cs
private void dvmVoltageSet(object sender, VoltageEventArgs e)
{
VoltageSet = e.VolSet;
TestLvdtNull();
}
Calling TestLvdtNull method:
private void TestLvdtNull()
{
tbMessage.Location = new Point((int)(x / 2 - 250), 150);
}
As soon as the tbMessage line is reached it causes an exception because it has started another thread other than the one tbMessage was created in, how can I prevent it from starting a new thread and continue using the Main thread please?
I have looked at singlethreadsynchronizationcontext, but couldn't make it compile and I know that you can invoke:
tbMessage.Invoke((Action)delegate
{
tbMessage.Location = new Point((int)(x / 2 - 250), 150);
});
But I have many controls with many attributes changing, there must be a way to keep the UI on the main thread?
All UI controls are created at one thread. That is by design in many UI frameworks. After you finish your task you have to return to the UI thread to access UI controls.
One option mentioned in comments is to use async/await where the part of the method after await keyword is executed on the same thread as was before the async method.
// UI thread
await ReadDVMWorker(); // executed at ThreadPool
// UI thread
If you prefer to stay with Task, you can use ContinueWith method with correct TaskScheduler parameter, which ensures that you're back to UI thread. Eg. TaskScheduler.FromCurrentSynchronizationContext()
Async/await attempt code:
private async void button1_Click(object sender, EventArgs e)
{
// Call the method that runs asynchronously.
string result = await WaitAsynchronouslyAsync();
// Display the result.
textBox1.Text += result;
}
//The following method runs asynchronously.The UI thread is not
//blocked during the delay.You can move or resize the Form1 window
//while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
await dvm.SetVoltage(5, rigNo); //Task.Delay(10000);
return "Finished";
}
You could have a method to update arbitrary controls
private void dvmVoltageSet(object sender, VoltageEventArgs e)
{
VoltageSet = e.VolSet;
TestLvdtNull(tbMessage);
TestLvdtNull(tbMessage2);
}
private void TestLvdtNull(Control control)
{
control.BeginInvoke((MethodInvoker)delegate()
{
control.Location += new Point((int)(x / 2 - 250), 150);
});
}
After trying several different ways to solve the problem, I solved the problem by using SynchronizationContext.
This grabs the SyncronizationContext of the thread:
private SynchronizationContext _synchronizationContext;
SynchronizationContext uiContext = SynchronizationContext.Current;
Then after running my task in another class, where previously I was getting an cross thread call exception, I call the method that wants use the same UI thread:
uiContext.Post(MethodToCallOnTheSameUIThread, "string");
After this I can modify and update my textboxes and other controls!
You can check the thread id by:
int id = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Thread: " + id);
With thanks to Mike Peretz and his CodeProject

How to update label control of the parent UI window from child window in sequential manner in wpf?

I have tried to find out the answer but no real answer found. Can anyone answer following? I am trying to update status in the parent window by calling Dispatcher Method, but I see it does not update the status in sequential manner. I see the status update in the mainwindow as follow:
First process started...
First Process done!
Third Process done!
Instead of delay between the UpdateStatus, there can be some other method or task. So, Why it does not update other status? Any real answer please?
private void Button_Click(object sender, RoutedEventArgs e)
{
UpdateStatus("First Process started...");
Thread.Sleep(5000); //or any method
UpdateStatus("First Process done!");
Thread.Sleep(5000); //or any method
UpdateStatus("Second Process started...");
Thread.Sleep(5000); //or any method
UpdateStatus("Second Process done!");
Thread.Sleep(5000); //or any method
UpdateStatus("Third Process started...");
Thread.Sleep(5000); //or any method
UpdateStatus("Third Process done!");
}
private void UpdateStatus(string message)
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate
{
MainWindow.main.lblTest.Content = message;
}
));
}
A single thread can't both update the UI and sleep at the same time.
You could wait asynchronously between the status updates though:
private async void Button_Click(object sender, RoutedEventArgs e)
{
UpdateStatus("First Process started...");
await Task.Delay(5000);
UpdateStatus("First Process done!");
await Task.Delay(5000);
UpdateStatus("Second Process started...");
await Task.Delay(5000);
UpdateStatus("Second Process done!");
await Task.Delay(5000);
UpdateStatus("Third Process started...");
await Task.Delay(5000);
UpdateStatus("Third Process done!");
}
private void UpdateStatus(string message)
{
MainWindow.main.lblTest.Content = message;
}
I hope this will make it more clear.
private async void Button_Click(object sender, RoutedEventArgs e)
{
// I run on the main thread and return immediately
await SearchForAliensAsync();
}
private async Task SearchForAliensAsync()
{
// I run on (capture) the caller thread (main thread)
UpdateStatus("Searching for aliens...");
/* Dispatch the dirty work on a background thread and return immediately,
* so that the caller thread (main thread) remains responsive.
* If you don't do that, the main thread will get blocked.
* From the user perspective the UI is frozen.
* The main thread will search infinitely for aliens,
* instead of doing its main job - poping and dispatching messages from
* its message queue (Win32 is event driven).
* The operating system will keep notifying the main thread,
* that the user has clicked on the window or smashed multiple keys on the keyboard,
* but the main thread won't listen, it's searching for aliens!
* The operating system monitors all main threads, by measuring what
* time has elapsed from the last call to GetMessage(...) and
* if the latter exceeds a certain amount, it will play a trick on you.
* The OS will create a transparent window and place it on top of the
* unresponsive one, so that it can detect the next angry mouse click
* and display the famous "Application is not responding" dialog...
*/
await Task.Run(() =>
{
// I run synchronously on the thread pool and I won't finish any time soon..
SearchForAliens();
});
// When my time comes, I'll be dispatched on the captured thread (main thread)
UpdateStatus("Aliens exist!");
}
You are running everything synchronously in UI thread, preventing any invoke until you finish (therefore no updates and then all updates at once). Simply move your code in task:
void Button_Click(object sender, RoutedEventArgs e) => Task.Run(() =>
{
... // rest of your code
});
and you should be fine (fine unless you press button twice, but that's easy to fix I guess).
Btw, you can also use another overload of invoke:
Dispatcher.Invoke(() => lblTest.Content = message);

WPF Dispatcher Thread Freeze main window

Instead of working in the background - this code still freeze my program:
private void button_Click(object sender, RoutedEventArgs e)
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
Thread.Sleep(5000);
label.Content = "Done";
}), DispatcherPriority.Normal);
}
I have tried with Thread/Tasks, thread example:
private void button_Click(object sender, RoutedEventArgs e)
{
var t = new Thread(new ThreadStart(runtask));
t.Start();
}
private void runtask()
{
this.Dispatcher.BeginInvoke(new Action(() =>
{
Thread.Sleep(5000);
label.Content = "Done";
}), DispatcherPriority.Normal);
}
Task example:
private void button_Click(object sender, RoutedEventArgs e)
{
Task.Run(() =>
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
{
Thread.Sleep(5000);
label.Content = "Done";
}));
});
}
And still my program is freezing. Any suggestions?
From the documentation of the Dispatcher class:
Provides services for managing the queue of work items for a thread.
From the documentation of Dispatcher.BeginInvoke:
Executes the specified delegate asynchronously with the specified arguments on the thread that the Dispatcher was created on.
Here "asynchronously" refers to the secondary thread, not the main one. Because the main one is owned by the main Dispatcher. That means that every call of Invoke or BeginInvoke on that Dispatcher, from whatever Thread, will put the invoked Action in the queue of operations that the main Thread must execute, but from the point of view of the main Thread they will be executed synchronously, one after the other.
For example, if you put 3 Action like Thread.Sleep(1000); within 10 ms on the Dispatcher, whether with Invoke or BeginInvoke and from whether Thread, that Dispatcher will make the UI Thread to execute the 3 Action synchronously, so they will take a total of 3000 ms.
Maybe the documentation about BeginInvoke could have been written better, like:
Executes the specified delegate with the specified arguments on the thread that the Dispatcher was created on. The specified delegate is executed asynchronously from the point of view of the calling thread.
Now... Invoke or BeginInvoke?
Using Invoke, the secondary Thread is saying to the Dispatcher: let's execute this on the main Thread, and don't dare to return until your thread's job has finished. Then and only then I will continue.
For example, if you write this:
this.Dispatcher.Invoke(new Action(() =>
{
Thread.Sleep(5000);
Debug.WriteLine("After Sleep");
}));
Debug.WriteLine("Continuation on secondary Thread");
The Console will print after ~ 5000 ms:
"After Sleep"
"Continuation on secondary Thread"
Using BeginInvoke, instead, the Thread is saying: "hey, Dispatcher, queue this operation on the main Thread, but return as soon as possible so I can continue my job immediately".
In this case the Console will print immediately:
"Continuation on secondary Thread"
And after ~ 5000 ms:
"After Sleep"
Now, if your purpose is to execute some heavy operation on the background, you should learn about the async/await pattern, available from .NET 4.5 and C# 5.0.
In your example, I would write:
private async void button_Click(object sender, RoutedEventArgs e)
{
await Task.Delay(5000); // await a heavy operation executed in background
label.Content = "Done"; // control back to the UI Thread that executes this
}
You can use this small extension if your UI access is the last of your method.
https://mitsufu.wordpress.com/2015/08/03/dispatcher-switchto/
private void Button_Click(object sender, RoutedEventArgs e)
{
Task.Run(async () =>
{
//heavy background operation
await Dispatcher.SwitchTo();
Title = "ok";
});
}

Thread Blocking the UI

I was following an example from C# in a Nutshell. According to the text the following code is supposed to be non blocking, but I find that the form will not display until the 5 seconds have passed.
private void Form1_Load(object sender, EventArgs e)
{
var tcs = new TaskCompletionSource<int>();
new Thread(() => {Thread.Sleep(5000); tcs.SetResult(42); }).Start();
Task<int> task = tcs.Task;
MessageBox.Show(task.Result.ToString());
}
I have a feeling it's something to do with Thread.Sleep() and instead of putting the new Thread to sleep, it's putting the main Thread to sleep.
Why is it blocking the UI thread?
When you are trying to get result of task task.Result main thread will be blocked until task will finish it's execution (i.e. result will be available). Use task.ContinueWith if you don't want to wait for async operation completion:
Task<int> task = tcs.Task;
task.ContinueWith(t => {
MessageBox.Show(t.Result.ToString());
});
BTW there is nice feature in .NET 4.5 for resuming suspended operation when task is completed - async methods:
private async void Form1_Load(object sender, EventArgs e)
{
var tcs = new TaskCompletionSource<int>();
new Thread(() => { Thread.Sleep(2000); tcs.SetResult(42); }).Start();
int result = await tcs.Task;
MessageBox.Show(result.ToString());
}
This method will yield control to caller immediately after starting to wait for task result. When result will be available, method will continue execution and show message.
Actually as #Servy pointed in comments, async method which return void is not very good practice (e.g. for error handling), but sometimes it's OK to use them for event handlers.
When you call Task.Result.ToString() (in the MessageBox.Show) the Task class has a mechanism that waits for the task to be finished before actually giving you the result (as it doesn't actually have it until the Task finishes. Here's my proof:
private void Form1_Load(object sender, EventArgs e)
{
var tcs = new TaskCompletionSource<int>();
new Thread(() => {Thread.Sleep(5000); tcs.SetResult(42); }).Start();
Task<int> task = tcs.Task;
Thread.Sleep(2500);
MessageBox.Show("Waited for 2.5secs on UI thread.");
MessageBox.Show(task.Result.ToString());
}
You will see that it shows you the 2.5sec message box before the messagebox with 42. (in fact, 2.5 seconds before that).
What you are looking for is this:
private void Form1_Load(object sender, EventArgs e)
{
var tcs = new TaskCompletionSource<int>();
new Thread(() => {Thread.Sleep(5000); tcs.SetResult(42); }).Start();
Task<int> task = tcs.Task;
task.ContinueWith(t => MessageBox.Show(t.Result.ToString()));
}
This will not freeze the UI thread.

Categories