我今天尝试使用SwitchTo方法切换到GUI线程,并发现我解除它的示例不起作用,只是因为该方法不存在。
然后我发现了这个模糊here:
我们摆脱它的原因是因为它太危险了。另一种方法是将您的代码捆绑在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()
{
}
}
这将允许我编写这样的代码:
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
}
答案 0 :(得分:6)
Stephen Toub在this thread中提供了有关推理的更多信息。
总而言之,出于两个原因,这不是一个好主意:
Task.Run
中。更好的是,将您的业务逻辑与UI逻辑分开。catch
中的finally
/ Test
块需要处理在线程池或 UI上下文中运行(如果它们在线程池上下文中运行,他们不能使用SwitchTo
来跳转UI上下文。此外,只要您await
返回的Task
您应该没问题(await
将在必要时更正延续上下文),但是如果您有明确的ContinueWith
延续使用ExecuteSynchronously
,然后他们会遇到与catch
/ finally
阻止相同的问题。简而言之,如果没有SwitchTo
,代码就更清晰,更具可预测性。
答案 1 :(得分:2)
ConfigureAwait实际上比SwitchTo更危险。精神上跟踪当前上下文和最后一次SwitchTo调用并不比跟踪上次分配变量的位置困难。另一方面,当且仅当调用实际异步运行时,ConfigureAwait才会切换上下文。如果任务已完成,则保留上下文。你无法控制这一点。
答案 2 :(得分:1)
在this GitHub issue中,David Fowler和Stephen Toub认为SwitchTo
即将回到CLR,因为await
在{ {1}} / try
。
IMO,显式使用catch
之类的东西要比依赖第三方库代码中的await TaskScheduler.Default.SwitchTo()
更好,特别是如果我们要确保该代码不在任何自定义同步上下文中执行时。我有一个博客帖子with more details on that,其中包括an experimental implementation of SwitchTo
。
简而言之,我相信下面的第一个选项以最少的样板代码清楚地表明了意图:
ConfigureAwait(false)
// switch to the thread pool explicitly for the rest of the async method
await TaskScheduler.Default.SwitchTo();
await RunOneWorkflowAsync();
await RunAnotherWorkflowAsync();
// execute RunOneWorkflowAsync on the thread pool
// and stay there for the rest of the async method
await Task.Run(RunOneWorkflowAsync).ConfigureAwait(false);
await RunAnotherWorkflowAsync();
await Task.Run(async () =>
{
// start on the thread pool
await RunOneWorkflowAsync();
await RunAnotherWorkflowAsync();
}).ConfigureAwait(false);
// continue on the thread pool for the rest of the async method