How to get a Task that uses SynchronizationContext? And how are SynchronizationContext used anyway?
Asked Answered
A

2

37

I am still learning the whole Task-concept and TPL. From my current understanding, the SynchronizationContext functions (if present) are used by await to dispatch the Task "somewhere". On the other hand, the functions in the Task class do not use the context, right?

So for example Task.Run(...) will always dispatch the action on an worker thread of the thread pool and ignore the SynchronizationContext.Current completely. await Foobar() would use the context to execute the generated task after the await?

If that is true, my question is: How can I obtain a Task, that actually runs an action but is dispatched using SynchronizationContext.Current.Send/Post ?

And can anyone recommend a good introduction into SynchronizationContext, especially when and how they are used by the rest of the framework? The MSDN seems to be very quiet about the class. The top Google hits (here and here) seem to be tailored to Windows Forms dispatching only. Stephen Cleary wrote an article which is nice to learn what contexts already exist and how they work, but I lack understanding of where and when they are actually used.

Aldine answered 4/6, 2013 at 11:7 Comment(0)
B
52

How can I obtain a Task, that actually runs an action but is dispatched using SynchronizationContext.Current.Send/Post?

Use special task scheduler:

Task.Factory.StartNew(
    () => {}, // this will use current synchronization context
    CancellationToken.None, 
    TaskCreationOptions.None, 
    TaskScheduler.FromCurrentSynchronizationContext());

And can anyone recommend a good introduction into SynchronizationContext

Look at the article It's All About the SynchronizationContext by Stephen Cleary.

Brockman answered 4/6, 2013 at 11:19 Comment(4)
ah, nice. thanks. Is there any difference between Task.Run and Task.Factory.StartNew ? (I mean.. any other difference than that "Task.Factory.StartNew accept some parameter which Task.Run does not" ;))Aldine
Look at this article. Thank you for the link. I already read that article and linked it in my question - very good for understanding what contexts exist and why they were introduced in the first place. Any other good sources, maybe going into detail about which class uses the contexts when? Or do I approach the TPL wrongly and I should read about "Why I should actually never care about this"? :D.Aldine
There are some other differences between Task.Run and Task.Factory.StartNew: Task.Run understands and automatically unwraps async delegates, Task.Run always uses the thread pool scheduler (while Task.Factory.StartNew will use the current task scheduler), and Task.Run will pass the DenyChildAttach option by default.Enjambment
Should there be TaskCreationOptions.None instead of TaskContinuationOptions.None?Rouble
E
28

As you're learning this, it's important to point out that Task as used by the TPL is quite different than Task as used by async/await, even though they're the same type. For example, TPL commonly uses parent/child tasks, but async/await does not.

TPL uses task schedulers to execute its tasks. As Dennis pointed out, TaskScheduler.FromCurrentSynchronizationContext will give you a task scheduler that uses Post on the current SynchronizationContext to execute its task.

async/await usually does not use task schedulers. I have an introductory async/await post on my blog that includes context information, and I also mention it briefly in my MSDN article (it's easy to overlook, though). Essentially, when an async method suspends at an await, by default it will capture the current SynchronizationContext (unless it is null, in which case it will capture the current TaskScheduler). When the async method resumes, it resumes executing in that context.

Dennis pointed out the TPL way of scheduling a task to the current SynchronizationContext, but in async/await world, that approach isn't necessary. Rather, you can explicitly schedule tasks to the thread pool via Task.Run:

async Task MyMethodAsync()
{
  // Whee, on a SynchronizationContext here!
  await Task.Run(() => { }); // Ooo, on the thread pool!
  // Back on the SynchronizationContext ...
  //  ... automagically!
}

I wrote my SynchronizationContext article precisely because the MSDN docs were so lacking. I have a little more information on my blog, but all the important bits are in the MSDN article. Many types use AsyncOperation rather than SynchronizationContext directly; the best documentation for this is buried under the EAP docs (section "Threading and Context"). But I should also point out that EAP is effectively obsolete due to async/await, so I wouldn't write code using AsyncOperation (or SynchronizationContext) - unless I was actually writing my own SynchronizationContext.

Enjambment answered 4/6, 2013 at 12:2 Comment(2)
SynchronizationContext (unless it is null, in which case it will capture the current TaskScheduler). Interesting... Actually, when reading up on TaskScheduler's, I start to think that a TaskScheduler - not an SynchronizationContext - is what I need for my original problem (not described here). Do you have good references of TaskSchedulers vs. SynchronizationContexts and what the difference actually is? (From looking at MSDN, both seem to be responsible for sheduling tasks onto threads, right?)Aldine
SynchronizationContext was developed with .NET 2.0 and is used to schedule delegates (synchronously or asynchronously). TaskScheduler was introduced in .NET 4.0 and is used to schedule tasks (asynchronously).Enjambment

© 2022 - 2024 — McMap. All rights reserved.