Ste*_*nds 11 c# code-formatting async-await
我目前正在编写大量的async库代码,并且我知道ConfigureAwait(false)在每次异步调用之后添加的做法,以避免将延续代码编组回原始(通常是UI)线程上下文.因为我不喜欢未标记的布尔参数,所以我倾向于将其写为ConfigureAwait(continueOnCapturedContext: false)相反.
我添加了一个扩展方法,使其更具可读性(并在某种程度上减少了输入):
public static class TaskExtensions
{
public static ConfiguredTaskAwaitable<TResult> WithoutCapturingContext<TResult>(this Task<TResult> task)
{
return task.ConfigureAwait(continueOnCapturedContext: false);
}
public static ConfiguredTaskAwaitable WithoutCapturingContext(this Task task)
{
return task.ConfigureAwait(continueOnCapturedContext: false);
}
}
Run Code Online (Sandbox Code Playgroud)
所以现在我可以有类似的东西await SomethingAsync().WithoutCapturingContext()而不是await SomethingAsync().ConfigureAwait(continueOnCapturedContext: false).我认为这是一个改进,但是当我必须async在同一个代码块中调用多个方法时,即使这种情况开始变得很糟糕,因为我最终得到类似于此的东西:
await FooAsync().WithoutCapturingContext();
var bar = await BarAsync().WithoutCapturingContext();
await MoreFooAsync().WithoutCapturingContext();
var moreBar = await MoreBarAsync().WithoutCapturingContext();
// etc, etc
Run Code Online (Sandbox Code Playgroud)
在我看来,它开始使代码的可读性低得多.
我的问题基本上是这样的:有没有办法进一步减少这种情况(除了缩短扩展方法的名称)?
没有全局设置可以阻止方法中的任务捕获同步上下文,但您可以做的是仅更改该方法范围的同步上下文.在您的特定情况下,您可以将上下文更改为默认同步上下文,仅适用于该方法的范围.
编写一个简单的一次性类可以很容易地改变同步上下文,然后在处理时将其更改回来:
public class SyncrhonizationContextChange : IDisposable
{
private SynchronizationContext previous;
public SyncrhonizationContextChange(SynchronizationContext newContext = null)
{
previous = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(newContext);
}
public void Dispose()
{
SynchronizationContext.SetSynchronizationContext(previous);
}
}
Run Code Online (Sandbox Code Playgroud)
允许你写:
using(var change = new SyncrhonizationContextChange())
{
await FooAsync();
var bar = await BarAsync();
await MoreFooAsync();
var moreBar = await MoreBarAsync();
}
Run Code Online (Sandbox Code Playgroud)
(注意设置上下文null意味着它将使用默认上下文.)
请注意,ConfigureAwait(false)这并不意味着忽略同步上下文。有时,尽管实际延续已在具有非null同步上下文的非池线程上触发,但它仍可以将await延续推送到池线程。海事组织,这种行为ConfigureAwait(false)可能令人惊讶且非直觉。至少,它的副作用是冗余线程切换。
如果您确实想在此之后忽略继续线程的同步上下文,await而只是在TaskContinuationOptions.ExecuteSynchronously碰巧发生的线程/上下文上同步恢复执行(),则可以使用自定义的等待者:
await MoreFooAsync().IgnoreContext();
Run Code Online (Sandbox Code Playgroud)
以下是可能的实现IgnoreContext(仅经过非常轻微的测试):
public static class TaskExt
{
// Generic Task<TResult>
public static IgnoreContextAwaiter<TResult> IgnoreContext<TResult>(this Task<TResult> @task)
{
return new IgnoreContextAwaiter<TResult>(@task);
}
public struct IgnoreContextAwaiter<TResult> :
System.Runtime.CompilerServices.ICriticalNotifyCompletion
{
Task<TResult> _task;
public IgnoreContextAwaiter(Task<TResult> task)
{
_task = task;
}
// custom Awaiter methods
public IgnoreContextAwaiter<TResult> GetAwaiter()
{
return this;
}
public bool IsCompleted
{
get { return _task.IsCompleted; }
}
public TResult GetResult()
{
// result and exceptions
return _task.GetAwaiter().GetResult();
}
// INotifyCompletion
public void OnCompleted(Action continuation)
{
// not always synchronous, http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx
_task.ContinueWith(_ => continuation(), TaskContinuationOptions.ExecuteSynchronously);
}
// ICriticalNotifyCompletion
public void UnsafeOnCompleted(Action continuation)
{
// why SuppressFlow? http://blogs.msdn.com/b/pfxteam/archive/2012/02/29/10274035.aspx
using (ExecutionContext.SuppressFlow())
{
OnCompleted(continuation);
}
}
}
// Non-generic Task
public static IgnoreContextAwaiter IgnoreContext(this Task @task)
{
return new IgnoreContextAwaiter(@task);
}
public struct IgnoreContextAwaiter :
System.Runtime.CompilerServices.ICriticalNotifyCompletion
{
Task _task;
public IgnoreContextAwaiter(Task task)
{
_task = task;
}
// custom Awaiter methods
public IgnoreContextAwaiter GetAwaiter()
{
return this;
}
public bool IsCompleted
{
get { return _task.IsCompleted; }
}
public void GetResult()
{
// result and exceptions
_task.GetAwaiter().GetResult();
}
// INotifyCompletion
public void OnCompleted(Action continuation)
{
// not always synchronous, http://blogs.msdn.com/b/pfxteam/archive/2012/02/07/10265067.aspx
_task.ContinueWith(_ => continuation(), TaskContinuationOptions.ExecuteSynchronously);
}
// ICriticalNotifyCompletion
public void UnsafeOnCompleted(Action continuation)
{
// why SuppressFlow? http://blogs.msdn.com/b/pfxteam/archive/2012/02/29/10274035.aspx
using (ExecutionContext.SuppressFlow())
{
OnCompleted(continuation);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1480 次 |
| 最近记录: |