为什么从Async CTP/Release中删除"SwitchTo"?

ang*_*son 13 c# asynchronous async-await async-ctp

我今天尝试使用SwitchTo方法切换到GUI线程,并发现我解除它的示例不起作用,仅仅是因为该方法不存在.

然后我在这里发现了这个模糊:

我们摆脱它的原因是因为它太危险了.另一种方法是在TaskEx.Run中捆绑你的代码......

我的问题很简单:为什么它很危险?使用它会带来哪些特定的危险?

请注意,我确实阅读了该帖子的其余部分,因此我确实理解这里存在技术限制.我的问题仍然是,如果我意识到这一点,为什么它很危险

我正在考虑重新实现帮助方法来给我指定的功能,但如果有一些根本性的破坏,除了有人认为它是危险的,我不会这样做.

具体来说,非常天真,这是我如何考虑实现所需的方法:

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()
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

这将允许我编写这样的代码:

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
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 6

Stephen Toub在这个帖子中有更多关于推理的信息.

总而言之,出于两个原因,这不是一个好主意:

  1. 它促进了非结构化代码.如果您需要进行"重处理",则应将其放入Task.Run.更好的是,将业务逻辑与UI逻辑分开.
  2. 错误处理和(某些)延续在未知的上下文中运行.catch/ finallyblocks in Test需要处理在线程池 UI上下文中运行(如果它们在线程池上下文中运行,则它们不能用于SwitchTo跳转UI上下文).此外,只要您await返回,Task您应该没问题(await如果需要,将更正连续上下文),但如果您有明确的ContinueWith继续使用ExecuteSynchronously,那么它们将具有与catch/ finallyblocks 相同的问题.

简而言之,代码更清晰,更具可预测性SwitchTo.


Bru*_*nez 5

ConfigureAwait 实际上比 SwitchTo 更危险。心理上跟踪当前上下文和最后一次 SwitchTo 调用并不比跟踪变量上次分配的位置更困难。另一方面,当且仅当调用实际异步运行时,ConfigureAwait 才会切换上下文。如果任务已经完成,则保留上下文。你无法控制这一点。