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.
Related
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";
});
}
I have a windows forms application. I want to do something in background after pressing some button.
//---------- MyWorker::Run
public Task Run()
{
WorkerTask = Task.Factory.StartNew(() =>
{
//Access MyWorker's properties etc. causes complications as it runs from
// a different thread and I need to use InvokeRequired etc. but not everything has Invoke method implemented.
}, TokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
return WorkerTask;
}
//---------- Form event
async void DoSomething(object sender, EventArgs e)
{
var task = MyWorker.Run();
await task;
}
However there are some complications when I try to access MyWorker's objects in the run method as it runs in a different thread. What pattern would be prefered here? I was thinking about something like:
//---------- Form event
async void DoSomething(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
var MyWorker1 = new MyWorker(...);
// Worker is synchronous here
MyWorker1.Run();
}, TokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
But I wouldn't be able to access MyWorker from other methods. Thanks in advance .. I'm confused.
You shouldn't implement a method entirely in terms of Task.Run (or StartNew); instead, call the method with Task.Run:
async void DoSomething(object sender, EventArgs e)
{
var task = Task.Run(() => MyWorker.Run());
await task;
}
This way you can access MyWorker from multiple methods... and multiple threads, of course. It's up to you to apply appropriate synchronization then.
On a side note, you shouldn't ever need to use Invoke or BeginInvoke in modern apps; there are always superior solutions. For example, await will return to the UI thread context automatically. Similarly, Progress<T> can synchronize to the UI context automatically.
You can customize this approach:
var myTasks = new ConcurrentBag<Task>();
var myResults = new ConcurrentBag<int>();
and use it like:
myTasks.Add(Task.Factory.StartNew(() =>
{
//do something
myResults.Add(5);
});
I have a logic on form load which is using parallel library, that is working fine. but after executing the tasks it is not opening the form. Can anyone help on this?
private void Form1_Load(object sender, EventArgs e)
{
Task[] tasks = new Task[3];
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
dummyList.ForEach(d =>
{
tasks[d] =
Task.Factory.StartNew(() =>
{
ProcessPage();
}, CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
});
Task.WaitAll(tasks);
Form2 frm = new Form2();
frm.Show();
}
It's pretty simple, you're deadlocking yourself.
You're instructing the tasks to run on the current synchronization context, which is your UI thread, via TaskScheduler.FromCurrentSynchronizationContext().
Then, you Wait on the main thread, synchronously blocking it.
Instead, you can asynchronously wait:
private async void Form1_Load(object sender, EventArgs e)
{
var tasks = dummyList.Select(() => Task.Run(() => ProcessPage());
await Task.WhenAll(tasks);
var form = new Form2();
form.Show();
}
One of your tasks is probably stuck. Nothing will appear until all the tasks have completed due to your WaitAll()
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());
}
I'm trying to understand when to use TaskEx.Run. I have provided two code sample i wrote below that produce the same result. What i fail to see is why i would take the Task.RunEx TaskEx.RunEx approach, I'm sure there is a good reason and was hoping someone could fill me in.
async Task DoWork(CancellationToken cancelToken, IProgress<string> progress)
{
int i = 0;
TaskEx.RunEx(async () =>
{
while (!cancelToken.IsCancellationRequested)
{
progress.Report(i++.ToString());
await TaskEx.Delay(1, cancelToken);
}
}, cancelToken);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (button.Content.ToString() == "Start")
{
button.Content = "Stop";
cts.Dispose();
cts = new CancellationTokenSource();
listBox.Items.Clear();
IProgress<string> progress = new Progress<string>(s =>
{
listBox.Items.Add(s);
listBox.ScrollIntoView(listBox.Items[listBox.Items.Count - 1]);
});
DoWork(cts.Token, progress);
}
else
{
button.Content = "Start";
cts.Cancel();
}
}
I can achieve the same results like so
async Task DoWork(CancellationToken cancelToken)
{
int i = 0;
while (!cancelToken.IsCancellationRequested)
{
listBox.Items.Add(i++);
listBox.ScrollIntoView(listBox.Items[listBox.Items.Count - 1]);
await TaskEx.Delay(100, cancelToken);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (button.Content.ToString() == "Start")
{
button.Content = "Stop";
cts.Dispose();
cts = new CancellationTokenSource();
listBox.Items.Clear();
DoWork(cts.Token);
}
else
{
button.Content = "Start";
cts.Cancel();
}
}
Use TaskEx.Run when you want to run synchronous code in a thread pool context.
Use TaskEx.RunEx when you want to run asynchronous code in a thread pool context.
Stephen Toub has two blog posts related to the difference in behavior:
Potential pitfalls to avoid when passing around async lambdas
Task.Run vs Task.Factory.StartNew
This is only one of several options you have for creating tasks. If you do not have to use Run/RunEx, then you should not. Use simple async methods, and only use Run/RunEx if you need to run something in the background.
The difference between your two DoWork() methods is that the first one (that uses TaskEx.RunEx()) is not asynchronous at all. It executes fully synchronously, starts the other task on another thread, and immediately returns a completed Task. If you awaited or Wait()ed on that task, it wouldn't wait until the internal task is completed.
Task.Run spawns a new thread in most scenarios as I understand it.
It's important to note that simply because you mark a method as async, and use awaiters, this does NOT (necessarily) mean that new threads are being created, completions are scheduled on the SAME thread of execution that they were called from in many cases.
The trick here has to do with the SchedulingContext. If it's set for a multithreaded apartment, then you're going to delegate completions to viable threads on the threadpool. If you're in a singlethreaded apartment as all WPF and WinForms UI code is, then it will return to the calling thread for completion allowing work to be done directly on the UI without visible thread marshalling in the code.