When to use asynchronous programming with delegates? - c#

They seem to be doing the same thing, but I don't know when I'm supposed to use tasks and when normal delegates.
Consider the example below:
private async void Button_Click(object sender, RoutedEventArgs e) {
UseDelegates();
// await UseTasks();
}
private void UseDelegates() {
Action action = () => {
Thread.Sleep(TimeSpan.FromSeconds(2));
};
var result = action.BeginInvoke(unusedResult => {
MessageBox.Show("Used delegate.BeginInvoke!");
}, null);
action.EndInvoke(result);
}
private async Task UseTasks() {
await Task.Run(() => {
Thread.Sleep(TimeSpan.FromSeconds(2));
});
MessageBox.Show("Used await with tasks!");
}

There is no truly asynchronous work being done in your example. It seems like the only thing you want to do is to offload some synchronous work to a background thread in order to keep the UI thread responsive during the time it takes for the synchronous method - Thread.Sleep in this case - to complete.
As stated on MSDN; starting with the .NET Framework 4, the Task Parallel Library (TPL) is the preferred way to write multithreaded and parallel code. That's what your UseTasks() method does, i.e. it uses Task.Run to schedule the call to Thread.Sleep on a thread pool thread using the TPL's default task scheduler.
The BeginInvoke/EndInvoke pattern is known as the Asynchronous Programming Model (APM). This pattern is no longer recommended for new development as stated here.
So to answer your questions, you are generally "supposed to use tasks" when offloading work to a background thread in .NET 4+ applications.

In the example you have given. I would use Task every time. The library and syntax has been created with the very aim of getting rid of BeginInvoke and EndInvoke type patterns.
The only time you probably don't want to use Task over an older library is in desktop apps that use BackgroundWorker, which is specifically for running long running background work, that wants to report progress easily to the UI thread. This sort of things isn't as elegant with tasks.

Related

Better solution for sync/async problem in desktop app?

I have WinForms app where button click calls some async method of external library.
private async void button1_Click(object sender, EventArgs e)
{
await CallLibraryAsync();
}
private static async Task CallLibraryAsync()
{
var library = new Library();
await library.DoSomethingAsync();
}
The library looks like this:
public class Library
{
public async Task DoSomethingAsync()
{
Thread.Sleep(2000);
await Task.Delay(1000).ConfigureAwait(false);
// some other code
}
}
Before any asynchronous code there is some calculation simulated by Thread.Sleep call. In that case this call will block UI thread for 2 seconds. I have no option to change the code in DoSomethingAsync.
If I want to solve blocking problem, I could call the library in Task.Run like this:
private static async Task CallLibraryAsync()
{
var library = new Library();
// added Task.Run
await Task.Run(() => library.DoSomethingAsync());
}
It solves the problem, UI is not blocke anymore, but I've consumed one thread from ThreadPool. It is not good solution.
If I want to solve this problem without another thread, I can do something like this:
private static async Task CallLibraryAsync()
{
var library = new Library();
// added
await YieldOnlyAsync().ConfigureAwait(false);
await library.DoSomethingAsync();
}
// added
private static async Task YieldOnlyAsync()
{
await Task.Yield();
}
This solution works. Task.Yield() causes that method YieldOnlyAsync() always runs asynchronously and ConfigureAwait(false) causes that next code (await library.DoSomethingAsync();) runs on some ThreadPool thread, not UI thread.
But it is quite complicated solution. Is there any simpler?
Edit:
If the library method looks like this
public class Library
{
public async Task DoSomethingAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
Thread.Sleep(2000);
await Task.Delay(1000);
// some other code
}
}
UI thread would not be blocked and I do not need to do anything. But that's the problem that it is some implementation detail I do not see directly because that could be in some nuget package. When I see that the UI freezes in some situations, I may find this problem (mean CPU-bound calculation before any await in async method) just after some investigation. There is no Wait() or Result, that would be easy to find, this is more problematic.
What I would like is to be prepared for that situation if possible in some simpler way. And that's why I do not want to use Task.Run whenewer I call some third-party library.
If I want to solve blocking problem, I could call the library in Task.Run like this:
It solves the problem, UI is not blocke anymore, but I've consumed one thread from ThreadPool. It is not good solution.
This is exactly what you want to do in a WinForms app. CPU-intensive code should be moved to a separate thread to free up the UI thread. There isn't any downside to consuming a new thread in WinForms.
Use Task.Run to move it to a different thread, and wait asynchronously from the UI thread for it to complete.
To quote the Asynchronous programming article from Microsoft:
If the work you have is CPU-bound and you care about responsiveness, use async and await, but spawn off the work on another thread with Task.Run.
I have no option to change the code
People say that, but you might not actually be hamstrung thus..
Here's a simple app with the same problem you face:
It's definitely pretty sleepy:
So let's whack it into ILSpy with the Reflexil plugin loaded:
We can perhaps shorten that timeout a bit.. Right click, Edit..
Make it 1ms, Right click the assembly and Save As..
That's a bit quicker!
Have a play, NOP it out etc..
You wrote:
If I want to solve blocking problem, I could call the library in Task.Run like this:
private static async Task CallLibraryAsync()
{
var library = new Library();
// added Task.Run
await Task.Run(() => library.DoSomethingAsync());
}
It solves the problem, UI is not blocked anymore, but I've consumed one thread from ThreadPool. It is not good solution.
(emphasis added)
...and then you proceed with inventing a convoluted hack that does the same thing: offloads the invocation of the DoSomethingAsync method to the ThreadPool. So you either want to:
Invoke the DoSomethingAsync method without using any thread at all, or
Invoke the DoSomethingAsync method on a non-ThreadPool thread.
The first is impossible. You can't invoke a method without using a thread. Code runs on CPUs, not on thin air. The second can be done in many ways, with the easiest being to use the Task.Factory.StartNew method, in combination with the LongRunning flag:
await Task.Factory.StartNew(() => library.DoSomethingAsync(), default,
TaskCreationOptions.LongRunning, TaskScheduler.Default).Unwrap();
This way you will invoke the DoSomethingAsync on a newly created thread, which will be destroyed immediately after the invocation of the method has completed. To be clear, the thread will be destroyed when the invocation completes, not when the asynchronous operation completes. Based on the DoSomethingAsync implementation that you have included in the question (the first one), the invocation will complete immediately after creating the Task.Delay(1000) task, and initiating the await of this task. There will be nothing for the thread to do after this point, so it will be recycled.
Side notes:
The CallLibraryAsync method violates the guideline for not exposing asynchronous wrappers for synchronous methods. Since the DoSomethingAsync method is implemented as partially synchronous and partially asynchronous, the guideline still applies IMHO.
If you like the idea of controlling imperatively the current context, instead of controlling it with wrappers like the Task.Run method, you could check out this question: Why was SwitchTo removed from Async CTP / Release? There are (not very many) people who like it as well, and there are libraries available that make it possible (SwitchTo - Microsoft.VisualStudio.Threading).
When you use async/await for I/O or CPU-bound operations, your UI thread will not blocked. In your example, you use Thread.Sleep(2000);command for simulating your CPU-bound operations but this will block your thread-pool thread not UI thread. You can use Task.Delay(2000); for simulating your I/O operations without blocking thread-pool thread.

What is the difference between Task.Run() and Task.Factory.StartNew()

I have Method :
private static void Method()
{
Console.WriteLine("Method() started");
for (var i = 0; i < 20; i++)
{
Console.WriteLine("Method() Counter = " + i);
Thread.Sleep(500);
}
Console.WriteLine("Method() finished");
}
And I want to start this method in a new Task.
I can start new task like this
var task = Task.Factory.StartNew(new Action(Method));
or this
var task = Task.Run(new Action(Method));
But is there any difference between Task.Run() and Task.Factory.StartNew(). Both of them are using ThreadPool and start Method() immediately after creating instance of the Task. When we should use first variant and when second?
The second method, Task.Run, has been introduced in a later version of the .NET framework (in .NET 4.5).
However, the first method, Task.Factory.StartNew, gives you the opportunity to define a lot of useful things about the thread you want to create, while Task.Run doesn't provide this.
For instance, lets say that you want to create a long running task thread. If a thread of the thread pool is going to be used for this task, then this could be considered an abuse of the thread pool.
One thing you could do in order to avoid this would be to run the task in a separate thread. A newly created thread that would be dedicated to this task and would be destroyed once your task would have been completed. You cannot achieve this with the Task.Run, while you can do so with the Task.Factory.StartNew, like below:
Task.Factory.StartNew(..., TaskCreationOptions.LongRunning);
As it is stated here:
So, in the .NET Framework 4.5 Developer Preview, we’ve introduced the
new Task.Run method. This in no way obsoletes Task.Factory.StartNew,
but rather should simply be thought of as a quick way to use
Task.Factory.StartNew without needing to specify a bunch of
parameters. It’s a shortcut. In fact, Task.Run is actually
implemented in terms of the same logic used for Task.Factory.StartNew,
just passing in some default parameters. When you pass an Action to
Task.Run:
Task.Run(someAction);
that’s exactly equivalent to:
Task.Factory.StartNew(someAction,
CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
People already mentioned that
Task.Run(A);
Is equivalent to
Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
But no one mentioned that
Task.Factory.StartNew(A);
Is equivalent to:
Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current);
As you can see two parameters are different for Task.Run and Task.Factory.StartNew:
TaskCreationOptions - Task.Run uses TaskCreationOptions.DenyChildAttach which means that children tasks can not be attached to the parent, consider this:
var parentTask = Task.Run(() =>
{
var childTask = new Task(() =>
{
Thread.Sleep(10000);
Console.WriteLine("Child task finished.");
}, TaskCreationOptions.AttachedToParent);
childTask.Start();
Console.WriteLine("Parent task finished.");
});
parentTask.Wait();
Console.WriteLine("Main thread finished.");
When we invoke parentTask.Wait(), childTask will not be awaited, even though we specified TaskCreationOptions.AttachedToParent for it, this is because TaskCreationOptions.DenyChildAttach forbids children to attach to it. If you run the same code with Task.Factory.StartNew instead of Task.Run, parentTask.Wait() will wait for childTask because Task.Factory.StartNew uses TaskCreationOptions.None
TaskScheduler - Task.Run uses TaskScheduler.Default which means that the default task scheduler (the one that runs tasks on Thread Pool) will always be used to run tasks. Task.Factory.StartNew on the other hand uses TaskScheduler.Current which means scheduler of the current thread, it might be TaskScheduler.Default but not always. In fact when developing Winforms or WPF applications it is required to update UI from the current thread, to do this people use TaskScheduler.FromCurrentSynchronizationContext() task scheduler, if you unintentionally create another long running task inside task that used TaskScheduler.FromCurrentSynchronizationContext() scheduler the UI will be frozen. A more detailed explanation of this can be found here
So generally if you are not using nested children task and always want your tasks to be executed on Thread Pool it is better to use Task.Run, unless you have some more complex scenarios.
See this blog article that describes the difference. Basically doing:
Task.Run(A)
Is the same as doing:
Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
The Task.Run got introduced in newer .NET framework version and it is recommended.
Starting with the .NET Framework 4.5, the Task.Run method is the
recommended way to launch a compute-bound task. Use the StartNew
method only when you require fine-grained control for a long-running,
compute-bound task.
The Task.Factory.StartNew has more options, the Task.Run is a shorthand:
The Run method provides a set of overloads that make it easy to start
a task by using default values. It is a lightweight alternative to the
StartNew overloads.
And by shorthand I mean a technical shortcut:
public static Task Run(Action action)
{
return Task.InternalStartNew(null, action, null, default(CancellationToken), TaskScheduler.Default,
TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
}
According to this post by Stephen Cleary, Task.Factory.StartNew() is dangerous:
I see a lot of code on blogs and in SO questions that use Task.Factory.StartNew to spin up work on a background thread. Stephen Toub has an excellent blog article that explains why Task.Run is better than Task.Factory.StartNew, but I think a lot of people just haven’t read it (or don’t understand it). So, I’ve taken the same arguments, added some more forceful language, and we’ll see how this goes. :)
StartNew does offer many more options than Task.Run, but it is quite dangerous, as we’ll see. You should prefer Task.Run over Task.Factory.StartNew in async code.
Here are the actual reasons:
Does not understand async delegates. This is actually the same as
point 1 in the reasons why you would want to use StartNew. The problem
is that when you pass an async delegate to StartNew, it’s natural to
assume that the returned task represents that delegate. However, since
StartNew does not understand async delegates, what that task actually
represents is just the beginning of that delegate. This is one of the
first pitfalls that coders encounter when using StartNew in async
code.
Confusing default scheduler. OK, trick question time: in the
code below, what thread does the method “A” run on?
Task.Factory.StartNew(A);
private static void A() { }
Well, you know it’s a trick question, eh? If you answered “a thread
pool thread”, I’m sorry, but that’s not correct. “A” will run on
whatever TaskScheduler is currently executing!
So that means it could potentially run on the UI thread if an operation completes and it marshals back to the UI thread due to a continuation as Stephen Cleary explains more fully in his post.
In my case, I was trying to run tasks in the background when loading a datagrid for a view while also displaying a busy animation. The busy animation didn't display when using Task.Factory.StartNew() but the animation displayed properly when I switched to Task.Run().
For details, please see https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html
Apart from the similarities i.e. Task.Run() being a shorthand for Task.Factory.StartNew(), there is a minute difference between their behaviour in case of sync and async delegates.
Suppose there are following two methods:
public async Task<int> GetIntAsync()
{
return Task.FromResult(1);
}
public int GetInt()
{
return 1;
}
Now consider the following code.
var sync1 = Task.Run(() => GetInt());
var sync2 = Task.Factory.StartNew(() => GetInt());
Here both sync1 and sync2 are of type Task<int>
However, difference comes in case of async methods.
var async1 = Task.Run(() => GetIntAsync());
var async2 = Task.Factory.StartNew(() => GetIntAsync());
In this scenario, async1 is of type Task<int>, however async2 is of type Task<Task<int>>

async /await for methods in wpf aplication

I have such question. For example I create dynamically custom userControl using mvvm pattern. So I've sended a command to create an userControl. So creation looks like
private async Task<bool> OnAddUserControl1(List<ALV_VM_ADWERT> control)
{
try
{
_cancellationTokenSource = new CancellationTokenSource();
var userControl = _userControlsContainer.CreateUserControl1(control);
var task1 = Task.Factory.StartNew(() =>
{
userControl.ViewModel.InOperationEvent += OnUsercontrolInOperationChanged;
userControl.ViewModel.ValueTypeChangedEvent += OnValueTypeChanged;
userControl.ViewModel.SetExpandableName += OnSetExpandableName;
}, _cancellationTokenSource.Token, TaskCreationOptions.AttachedToParent, TaskScheduler.FromCurrentSynchronizationContext());
var task2 = Task.Factory.StartNew(() => FinalCreatingStep(userControl, control[0].RAUMNAME.Trim()), _cancellationTokenSource.Token, TaskCreationOptions.AttachedToParent, TaskScheduler.FromCurrentSynchronizationContext());
await Task.WhenAll(task1, task2);
return true;
}
catch (Exception)
{
return false;
}
}
and my question is - does it make sence to create child tasks, or it is better to have code without child tasks? And if the answer is yes, then should I make all methods async? If not, what methods I should not make async?
Do those event subscriptions really need to be asynchronous? You may be trying too hard to use asynchronous code.
The user control constructor is usually the most time consuming part and must be done on the UI thread. Asynchronous operations are generally only required when some form of IO or processing is involed;
Reading Files
Writing Files
Processing large data sets
Crossing the process boundary to talk to a server or connect device
In short, an asynchronous task is probably overkill here.
Does it make sence to create child tasks, or it is better to have code without child tasks?
It is depending on your requirements. If your UI will blocked (Freeze) for long time you have to create a child task, otherwise not!
And if the answer is yes, then should I make all methods async? If not, what methods I should not make async?
Here it is also depending on your requirements and your .Net Version. If you are using .NET 4.5 the easiest way to do that with Async await. If you are using .Net 3.5 than just use Task. If .Net 2 use BackgorundWorker else use Thread class. ONLY Asynchrony methods must get the word async. Other methods you do not have to change them. In other words only the method which blocked the UI.
Your current code doesn't make any sense.
The point of async code in a UI application is responsiveness - i.e., moving long-running operations off the UI thread. As #Gusdor pointed out, most of the use cases for async are I/O-based (or event-based) operations, where you don't want to block the UI thread just to wait for some result. Another use case is when you have some CPU-bound work to do, but you don't want to tie up the UI thread; in this case, you can use Task.Run.
But in your code, you're calling StartNew with TaskScheduler.FromCurrentSynchronizationContext, which means your "child" tasks are going to execute on the UI thread. So your OnAddUserControl1 is just starting tasks which will run on the same thread and asynchronously waiting for them to complete. This is a very complex way of doing nothing.
While we're on the subject of StartNew, there are a number of other problems:
The code is passing a CancellationToken without ever observing it in the delegate.
The code is specifying AttachedToParent which is incorrect for await-compatible tasks.
As mentioned above, the code is passing a TaskScheduler which will run the delegate right back on the UI thread.
If you need to use background (thread pool) tasks, you should use Task.Run instead of Task.Factory.StartNew; I go into more detail on my blog.
So for this example, it doesn't make sense to use async or await at all.
The best way to start using async is to identify the I/O-bound (or event-driven) parts first (e.g., HTTP requests, database calls), make them async, and then work your way up the call stack.

Asynchronous Threading

I would please like to know where I can get an example of multithreading or asynchronous threading.
In the application that I am busy with I have to run a thread in the background of my application to fetch a value that is changing. And whenever this value reaches a certain amount then it needs to call another function. All this has to run in the background of the program so that the user can still do something else on the application.
Any examples or links that could help would really be appreciated.
In order to summarize the options, I will try to list them here (maybe it would be a good idea to make this a community wiki).
First of all, you can simply start a function in another thread:
Thread t = new Thread( ThreadProc );
t.Start();
// now you can wait for thread to finish with t.Join() or just continue
// Thread.IsBackground allows to control how thread lifetime influences
// the lifetime of the application
...
static void ThreadProc() {...} // can also be non-static, but for simplicity....
Then you can use BackgroundWorker:
BackgroundWorker bgWorker = new BackgroundWorker();
bgWorker.DoWork += MyFunction;
bgWorker.RunWorkerAsync();
voud MyFunction(object o, DoWorkEventArgs args) {...}
You can use ProgressChanged and RunWorkerCompleted events for more control (as well as WorkerReportsProgress and other properties)
Another option is to use ThreadPool, if your method will not take too much time:
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
...
static void ThreadProc(Object stateInfo) { ... }
Yet another option is to call BeginInvoke on a delegate:
public delegate int MyDelegate(...);
MyDelegate del = SomeFunction;
IAsyncResult ar = del.BeginInvoke(...);
int result = del.EndInvoke(ar);
This will execute on a thread from the thread pool. If you need to wait on calling thread, you can use IAsyncResult.IsCompleted, but it will block the calling thread.
And of course, you can use Task:
var task = Task.Factory.StartNew(() => MyMethod());
This will also execute MyMethod on a thread from the thread pool, so the same warnings apply (although you can use TaskCreationOptions.LongRunning to ensure that the new thread is always created). Under some circumstances (when you wait on task) it can even execute on the same thread, but it is well optimized so you should not worry about that.
This is probably the option with best tradeoff of simplicity vs control (of course, there is no really 'the best'). Here are the benefits (shamelessly stolen from Jon Skeet's answer):
Adding continuations (Task.ContinueWith)
Waiting for multiple tasks to complete (either all or any)
Capturing errors in the task and interrogating them later
Capturing cancellation (and allowing you to specify cancellation to start with)
Potentially having a return value
Using await in C# 5
Better control over scheduling (if it's going to be long-running, say so when you create the task so the task scheduler can take that into account)
Well depending on the level of control that you seek a BackgroundWorker could easily work and its found within the System.ComponentModel.BackgroundWorker. Now here is a link to the MSDN docs on the subject matter : http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
a Simple usecase secenario is like so:
BackgrouWorker BG = new BackgroudWorker();
GB.DoWork += YourFunctionDelegate(object Sender, EventArgs e);
GB.RunWorkerAsync();
Now YourFunctionDelegate(object Sender,EventArgs e) should be what ever it is you want run in the background. However needs to follow this argument form, There are also a good amount of helper functions associated with the backgroundworker like onProgressChanged event that allows monitoring of obviously progress, which if you are new to threading can prove to be a pain at first if you try to make your own threads.
If you would like more control over execution and how the threads function you should take a look at the Task-Parallel-Library here: http://msdn.microsoft.com/en-us/library/dd460717.aspx Which has copious amount of information about multi-threading.
Also here is a great tutorial on how to create a C# thread: http://support.microsoft.com/default.aspx?scid=kb;en-us;815804
For an overview of asynchronous programming on Windows 8 in .Net 4.5:
http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx
For .Net 4.0 and older you can use the ThreadPool
System.Threading.ThreadPool.QueueUserWorkItem(obj =>
{
// Do some work
for (int i = 0; i < 1000; i++)
Math.Sin(i);
// Get back to the UI thread
App.Current.MainWindow.Dispatcher.BeginInvoke(
new Action(delegate
{
block.Text = "Done!";
}));
});
I have a blog post that compares and contrasts various implementations of background tasks, with advantages and disadvantages for each. Spoiler: Task is definitely the best option. Also, I recommend Task.Run over TaskFactory.StartNew.
If your background operation is truly asynchronous, then you may not need any background threading at all. LINQPad has a set of async examples that are a great starting point. These are more up-to-date than the chapter on threading (by the same author) that others have recommended.

Why is TaskScheduler.Current the default TaskScheduler?

The Task Parallel Library is great and I've used it a lot in the past months. However, there's something really bothering me: the fact that TaskScheduler.Current is the default task scheduler, not TaskScheduler.Default. This is absolutely not obvious at first glance in the documentation nor samples.
Current can lead to subtle bugs since its behavior is changing depending on whether you're inside another task. Which can't be determined easily.
Suppose I am writting a library of asynchronous methods, using the standard async pattern based on events to signal completion on the original synchronisation context, in the exact same way XxxAsync methods do in the .NET Framework (eg DownloadFileAsync). I decide to use the Task Parallel Library for implementation because it's really easy to implement this behavior with the following code:
public class MyLibrary
{
public event EventHandler SomeOperationCompleted;
private void OnSomeOperationCompleted()
{
SomeOperationCompleted?.Invoke(this, EventArgs.Empty);
}
public void DoSomeOperationAsync()
{
Task.Factory.StartNew(() =>
{
Thread.Sleep(1000); // simulate a long operation
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default)
.ContinueWith(t =>
{
OnSomeOperationCompleted(); // trigger the event
}, TaskScheduler.FromCurrentSynchronizationContext());
}
}
So far, everything works well. Now, let's make a call to this library on a button click in a WPF or WinForms application:
private void Button_OnClick(object sender, EventArgs args)
{
var myLibrary = new MyLibrary();
myLibrary.SomeOperationCompleted += (s, e) => DoSomethingElse();
myLibrary.DoSomeOperationAsync(); // call that triggers the event asynchronously
}
private void DoSomethingElse() // the event handler
{
//...
Task.Factory.StartNew(() => Thread.Sleep(5000)); // simulate a long operation
//...
}
Here, the person writing the library call chose to start a new Task when the operation completes. Nothing unusual. He or she follows examples found everywhere on the web and simply use Task.Factory.StartNew without specifying the TaskScheduler (and there is no easy overload to specify it at the second parameter). The DoSomethingElse method works fine when called alone, but as soon at it's invoked by the event, the UI freezes since TaskFactory.Current will reuse the synchronization context task scheduler from my library continuation.
Finding out this could take some time, especially if the second task call is buried down in some complex call stack. Of course, the fix here is simple once you know how everything works: always specify TaskScheduler.Default for any operation you're expecting to be running on the thread pool. However, maybe the second task is started by another external library, not knowing about this behavior and naively using StartNew without a specific scheduler. I'm expecting this case to be quite common.
After wrapping my head around it, I can't understand the choice of the team writing the TPL to use TaskScheduler.Current instead of TaskScheduler.Default as the default:
It's not obvious at all, Default is not the default! And the documentation is seriously lacking.
The real task scheduler used by Current depends of the call stack! It's hard to maintain invariants with this behavior.
It's cumbersome to specify the task scheduler with StartNew since you have to specify the task creation options and cancellation token first, leading to long, less readable lines. This can be alleviated by writing an extension method or creating a TaskFactory that uses Default.
Capturing the call stack has additional performance costs.
When I really want a task to be dependent on another parent running task, I prefer to specify it explicitly to ease code reading rather than rely on call stack magic.
I know this question may sound quite subjective, but I can't find a good objective argument as to why this behavior is as it. I'm sure I'm missing something here: that's why I'm turning to you.
I think the current behavior makes sense. If I create my own task scheduler, and start some task that starts other tasks, I probably want all the tasks to use the scheduler I created.
I agree that it's odd that sometimes starting a task from the UI thread uses the default scheduler and sometimes not. But I don't know how would I make this better if I was designing it.
Regarding your specific problems:
I think the easiest way to start a new task on a specified scheduler is new Task(lambda).Start(scheduler). This has the disadvantage that you have to specify type argument if the task returns something. TaskFactory.Create can infer the type for you.
You can use Dispatcher.Invoke() instead of using TaskScheduler.FromCurrentSynchronizationContext().
[EDIT]
The following only addresses the problem with the scheduler used by Task.Factory.StartNew.
However, Task.ContinueWith has a hardcoded TaskScheduler.Current.
[/EDIT]
First, there is an easy solution available - see the bottom of this post.
The reason behind this problem is simple: There is not only a default task scheduler (TaskScheduler.Default) but also a default task scheduler for a TaskFactory (TaskFactory.Scheduler).
This default scheduler can be specified in the constructor of the TaskFactory when it's created.
However, the TaskFactory behind Task.Factory is created as follows:
s_factory = new TaskFactory();
As you can see, no TaskScheduler is specified; null is used for the default constructor - better would be TaskScheduler.Default (the documentation states that "Current" is used which has the same consequences).
This again leads to the implementation of TaskFactory.DefaultScheduler (a private member):
private TaskScheduler DefaultScheduler
{
get
{
if (m_defaultScheduler == null) return TaskScheduler.Current;
else return m_defaultScheduler;
}
}
Here you should see be able to recognize the reason for this behaviour: As Task.Factory has no default task scheduler, the current one will be used.
So why don't we run into NullReferenceExceptions then, when no Task is currently executing (i.e. we have no current TaskScheduler)?
The reason is simple:
public static TaskScheduler Current
{
get
{
Task internalCurrent = Task.InternalCurrent;
if (internalCurrent != null)
{
return internalCurrent.ExecutingTaskScheduler;
}
return Default;
}
}
TaskScheduler.Current defaults to TaskScheduler.Default.
I would call this a very unfortunate implementation.
However, there is an easy fix available: We can simply set the default TaskScheduler of Task.Factory to TaskScheduler.Default
TaskFactory factory = Task.Factory;
factory.GetType().InvokeMember("m_defaultScheduler", BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly, null, factory, new object[] { TaskScheduler.Default });
I hope I could help with my response although it's quite late :-)
Instead of Task.Factory.StartNew()
consider using: Task.Run()
This will always execute on a thread pool thread. I just had the same problem described in the question and I think that is a good way of handling this.
See this blog entry:
http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
It's not obvious at all, Default is not the default! And the documentation is seriously lacking.
Default is the default, but it's not always the Current.
As others have already answered, if you want a task to run on the thread pool, you need to explicitly set the Current scheduler by passing the Default scheduler into either the TaskFactory or the StartNew method.
Since your question involved a library though, I think the answer is that you should not do anything that will change the Current scheduler that's seen by code outside your library. That means that you should not use TaskScheduler.FromCurrentSynchronizationContext() when you raise the SomeOperationCompleted event. Instead, do something like this:
public void DoSomeOperationAsync() {
var context = SynchronizationContext.Current;
Task.Factory
.StartNew(() => Thread.Sleep(1000) /* simulate a long operation */)
.ContinueWith(t => {
context.Post(_ => OnSomeOperationCompleted(), null);
});
}
I don't even think you need to explicitly start your task on the Default scheduler - let the caller determine the Current scheduler if they want to.
I've just spent hours trying to debug a weird issue where my task was scheduled on the UI thread, even though I didn't specify it to. It turned out the problem was exactly what your sample code demonstrated: A task continuation was scheduled on the UI thread, and somewhere in that continuation, a new task was started which then got scheduled on the UI thread, because the currently executing task had a specific TaskScheduler set.
Luckily, it's all code I own, so I can fix it by making sure my code specify TaskScheduler.Default when starting new tasks, but if you aren't so lucky, my suggestion would be to use Dispatcher.BeginInvoke instead of using the UI scheduler.
So, instead of:
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var task = Task.Factory.StartNew(() => Thread.Sleep(5000));
task.ContinueWith((t) => UpdateUI(), uiScheduler);
Try:
var uiDispatcher = Dispatcher.CurrentDispatcher;
var task = Task.Factory.StartNew(() => Thread.Sleep(5000));
task.ContinueWith((t) => uiDispatcher.BeginInvoke(new Action(() => UpdateUI())));
It's a bit less readable though.

Categories

Resources