Why was "SwitchTo" removed from Async CTP / Release?

async-await, async-ctp, asynchronous, c#

Solution

Stephen Toub has some more information on the reasoning in this thread.

To summarize, it's not a good idea for two reasons:

- It promotes unstructured code. If you have "heavy processing" that you need to do, it should be placed in a `Task.Run`. Even better, separate your business logic from your UI logic.

- Error handling and (some) continuations run in an unknown context. `catch`/`finally` blocks in `Test` would need to handle running in a thread pool or UI context (and if they're running in the thread pool context, they can't use `SwitchTo` to jump on the UI context). Also, as long as you `await` the returned `Task` you should be OK (`await` will correct the continuation context if necessary), but if you have explicit `ContinueWith` continuations that use `ExecuteSynchronously`, then they'll have the same problem as the `catch`/`finally` blocks.

In short, the code is cleaner and more predictable without `SwitchTo`.

Problem

I tried to use the SwitchTo method today to switch to the GUI thread, and found that the example I lifted it from does not work, simply because the method is not there. I then found this blurb here: The reason we got rid of it was because it was so dangerous. The alternative is to bundle up your code inside TaskEx.Run... My question is simply: Why was it dangerous? What specific dangers would using it lead to? Note that I did read the rest of that post, so I do understand there are technical limitations here. My question is still, if I'm aware of this, why is it dangerous? I am considering reimplementing helper methods to give me the specified functionality, but if there is something fundamentally broken, other than that someone decided it was dangerous, I would not do it. Specifically, very naively, here's how I would consider implementing the required methods: ``` public static class ContextSwitcher { public static ThreadPoolContextSwitcher SwitchToThreadPool() { return new ThreadPoolContextSwitcher(); } public static SynchronizationContextSwitcher SwitchTo(this SynchronizationContext synchronizationContext) { return new SynchronizationContextSwitcher(synchronizationContext); } } public class SynchronizationContextSwitcher : INotifyCompletion { private readonly SynchronizationContext _SynchronizationContext; public SynchronizationContextSwitcher(SynchronizationContext synchronizationContext) { _SynchronizationContext = synchronizationContext; } public SynchronizationContextSwitcher GetAwaiter() { return this; } public bool IsCompleted { get { return false; } } public void OnCompleted(Action action) { _SynchronizationContext.Post(_ => action(), null); } public void GetResult() { } } public class ThreadPoolContextSwitcher : INotifyCompletion { public ThreadPoolContextSwitcher GetAwaiter() { return this; } public bool IsCompleted { get { return false; } } public void OnCompleted(Action action) { ThreadPool.QueueUserWorkItem(_ => action(), null); } public void GetResult() { } } ``` This would allow me to write code like this: ``` public async void Test() { await ContextSwitcher.SwitchToThreadPool(); // ensure we're not bogging down the UI thread // do some heavy processing await _UIContext.SwitchTo(); // presumably saved from the main thread // update UI with new data } ```

Original source

Related problems