WARNING about using FromCurrentSynchronizationContext:
Ok, Cory knows how to make me rewrite answer:).
So the main culprit is actually the FromCurrentSynchronizationContext!
Any time StartNew or ContinueWith runs on this kind scheduler, it runs on the UI Thread. One may think:
OK, let's start subsequent operations on UI, change some controls, spawn some operations. But from now TaskScheduler.Current is not null and if any control has some events, that spawn some StartNew expecting to be running on ThreadPool, then from there it goes wrong. UI aps are usually complex, unease to maintain certainty, that nothing will call another StartNew operation, simple example here:
public partial class Form1 : Form
{
public static int Counter;
public static int Cnt => Interlocked.Increment(ref Counter);
private readonly TextBox _txt = new TextBox();
public static void WriteTrace(string from) => Trace.WriteLine($"{Cnt}:{from}:{Thread.CurrentThread.Name ?? "ThreadPool"}");
public Form1()
{
InitializeComponent();
Thread.CurrentThread.Name = "ThreadUI!";
//this seems to be so nice :)
_txt.TextChanged += (sender, args) => { TestB(); };
WriteTrace("Form1"); TestA(); WriteTrace("Form1");
}
private void TestA()
{
WriteTrace("TestA.Begin");
Task.Factory.StartNew(() => WriteTrace("TestA.StartNew"))
.ContinueWith(t =>
{
WriteTrace("TestA.ContinuWith");
_txt.Text = @"TestA has completed!";
}, TaskScheduler.FromCurrentSynchronizationContext());
WriteTrace("TestA.End");
}
private void TestB()
{
WriteTrace("TestB.Begin");
Task.Factory.StartNew(() => WriteTrace("TestB.StartNew - expected ThreadPool"))
.ContinueWith(t => WriteTrace("TestB.ContinueWith1 should be ThreadPool"))
.ContinueWith(t => WriteTrace("TestB.ContinueWith2"));
WriteTrace("TestB.End");
}
}
- Form1:ThreadUI! - OK
- TestA.Begin:ThreadUI! - OK
- TestA.End:ThreadUI! - OK
- Form1:ThreadUI! - OK
- TestA.StartNew:ThreadPool - OK
- TestA.ContinuWith:ThreadUI! - OK
- TestB.Begin:ThreadUI! - OK
- TestB.End:ThreadUI! - OK
- TestB.StartNew - expected ThreadPool:ThreadUI! - COULD BE UNEXPECTED!
- TestB.ContinueWith1 should be ThreadPool:ThreadUI! - COULD BE UNEXPECTED!
- TestB.ContinueWith2:ThreadUI! - OK
Please notice, that tasks returned by:
- async method,
- Task.Fatory.StartNew,
- Task.Run,
can not be started! They are already hot tasks...
return new Task
instead ofreturn Task.Factory.StartNew
. – Amberjack