仅在 Task RanToCompletion 上使用 SynchronizationContext
Use SynchronizationContext only on Task RanToCompletion
下面的代码解释了这个想法
private async void button1_Click(object sender, EventArgs e)
{
string result;
CancellationToken cancellationToken = new CancellationTokenSource(3000).Token;
try
{
result = await GetDataAsync(cancellationToken)
.ContextIfSuccess(); // Should use SynchronizationContext only if Task status is RanToCompletion
}
catch(OperationCanceledException)
{
/* Context is not required */
return;
}
catch (Exception ex)
{
/* Context is not required otherwise it can slow down UI Thread a little bit */
Log(ex.ToString());
return;
}
/* UI Thread only */
button1.Text = result;
}
问题是"Is it possible to make the method like ContextIfSuccess() ?"
await
没有上下文 (ConfigureAwait(false)
)。然后,如果所需条件为真,则切换到上下文。
因此您需要捕获 SynchronizationContext.Current
和 Post
。
这与 TaskAwaiter
所做的非常相似。它在没有上下文的情况下恢复,然后如果调用者需要则切换回上下文。
您应该可以将其变成 ContextIfSuccess
方法。基本上,克隆 TaskAwaiter
源代码并在完成通知中决定是否 Post
。我假设此功能已经存在。代码必须查看 ConfigureAwait(...)
值并有条件地应用上下文或不应用上下文。
为了获得您想要的方法,您需要创建自定义等待程序。它主要是样板文件,关键是当被要求添加一个延续时,你在成功完成时使用当前同步上下文添加一个到 运行,而在没有成功完成时使用默认调度程序添加一个 运行 完成.
public struct CaptureContextOnSuccessAwaiter : INotifyCompletion
{
private Task task;
public CaptureContextOnSuccessAwaiter(Task task)
{
this.task = task;
}
public CaptureContextOnSuccessAwaiter GetAwaiter() { return this; }
public void OnCompleted(Action continuation)
{
if (SynchronizationContext.Current != null)
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.NotOnRanToCompletion,
TaskScheduler.Default);
}
else
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
}
}
public void GetResult() { task.GetAwaiter().GetResult(); }
public bool IsCompleted { get { return task.GetAwaiter().IsCompleted; } }
}
public struct CaptureContextOnSuccessAwaiter<T> : INotifyCompletion
{
private Task<T> task;
public CaptureContextOnSuccessAwaiter(Task<T> task)
{
this.task = task;
}
public CaptureContextOnSuccessAwaiter<T> GetAwaiter() { return this; }
public void OnCompleted(Action continuation)
{
if (SynchronizationContext.Current != null)
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.NotOnRanToCompletion,
TaskScheduler.Default);
}
else
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
}
}
public T GetResult() { return task.GetAwaiter().GetResult(); }
public bool IsCompleted { get { return task.GetAwaiter().IsCompleted; } }
}
public static CaptureContextOnSuccessAwaiter ContextIfSuccess(this Task task)
{
return new CaptureContextOnSuccessAwaiter(task);
}
public static CaptureContextOnSuccessAwaiter<T> ContextIfSuccess<T>(this Task<T> task)
{
return new CaptureContextOnSuccessAwaiter<T>(task);
}
下面的代码解释了这个想法
private async void button1_Click(object sender, EventArgs e)
{
string result;
CancellationToken cancellationToken = new CancellationTokenSource(3000).Token;
try
{
result = await GetDataAsync(cancellationToken)
.ContextIfSuccess(); // Should use SynchronizationContext only if Task status is RanToCompletion
}
catch(OperationCanceledException)
{
/* Context is not required */
return;
}
catch (Exception ex)
{
/* Context is not required otherwise it can slow down UI Thread a little bit */
Log(ex.ToString());
return;
}
/* UI Thread only */
button1.Text = result;
}
问题是"Is it possible to make the method like ContextIfSuccess() ?"
await
没有上下文 (ConfigureAwait(false)
)。然后,如果所需条件为真,则切换到上下文。
因此您需要捕获 SynchronizationContext.Current
和 Post
。
这与 TaskAwaiter
所做的非常相似。它在没有上下文的情况下恢复,然后如果调用者需要则切换回上下文。
您应该可以将其变成 ContextIfSuccess
方法。基本上,克隆 TaskAwaiter
源代码并在完成通知中决定是否 Post
。我假设此功能已经存在。代码必须查看 ConfigureAwait(...)
值并有条件地应用上下文或不应用上下文。
为了获得您想要的方法,您需要创建自定义等待程序。它主要是样板文件,关键是当被要求添加一个延续时,你在成功完成时使用当前同步上下文添加一个到 运行,而在没有成功完成时使用默认调度程序添加一个 运行 完成.
public struct CaptureContextOnSuccessAwaiter : INotifyCompletion
{
private Task task;
public CaptureContextOnSuccessAwaiter(Task task)
{
this.task = task;
}
public CaptureContextOnSuccessAwaiter GetAwaiter() { return this; }
public void OnCompleted(Action continuation)
{
if (SynchronizationContext.Current != null)
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.NotOnRanToCompletion,
TaskScheduler.Default);
}
else
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
}
}
public void GetResult() { task.GetAwaiter().GetResult(); }
public bool IsCompleted { get { return task.GetAwaiter().IsCompleted; } }
}
public struct CaptureContextOnSuccessAwaiter<T> : INotifyCompletion
{
private Task<T> task;
public CaptureContextOnSuccessAwaiter(Task<T> task)
{
this.task = task;
}
public CaptureContextOnSuccessAwaiter<T> GetAwaiter() { return this; }
public void OnCompleted(Action continuation)
{
if (SynchronizationContext.Current != null)
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.NotOnRanToCompletion,
TaskScheduler.Default);
}
else
{
task.ContinueWith(t => continuation(),
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
}
}
public T GetResult() { return task.GetAwaiter().GetResult(); }
public bool IsCompleted { get { return task.GetAwaiter().IsCompleted; } }
}
public static CaptureContextOnSuccessAwaiter ContextIfSuccess(this Task task)
{
return new CaptureContextOnSuccessAwaiter(task);
}
public static CaptureContextOnSuccessAwaiter<T> ContextIfSuccess<T>(this Task<T> task)
{
return new CaptureContextOnSuccessAwaiter<T>(task);
}