在TaskCompletionSource.TrySetResult同步或异步延续? [英] Synchronous or asynchronous continuation upon TaskCompletionSource.TrySetResult?
问题描述
如何判断是否继续由启动 TaskCompletionSource.TrySetResult
将被同步或异步执行?
How to tell if the continuation initiated by TaskCompletionSource.TrySetResult
is going to be executed synchronously or asynchronously?
例如:
// class A
void RegisterNotification(TaskCompletionSource<object> tcs)
{
this.source.NotificationEvent += (s, eData) =>
{
Debug.WriteLine("A.before");
tcs.TrySetResult(eData.Result);
Debug.WriteLine("A.after");
DoProcessingA();
};
}
// class B
async Task RequestNotificationAsync()
{
var tcs = new TaskCompletionSource<object>();
this.a.RegisterNotification(tcs);
Debug.WriteLine("B.before");
var data = await tcs.Task;
Debug.WriteLine("B.after");
DoProcessingB();
}
如果 NotificationEvent
与来自哪里伺机tcs.Task
采取了不同的同步上下文一个线程被解雇到位,调试输出将是:
If NotificationEvent
is fired on a thread with the different synchronization context from that of where await tcs.Task
took place, the debug output will be:
B.before
A.before
A.after
B.after
也就是说,等待tcs.Task
延续异步执行。如果是在同一同步上下文解雇(或如果在这两个地方没有同步上下文),输出将是:
That is, the await tcs.Task
continuation is executed asynchronously. If it is fired on the same synchronization context (or if there is no synchronization context in both places), the output will be:
B.before
A.before
B.after
A.after
也就是说,延续同步执行。
That is, the continuation is executed synchronously.
有没有办法来predict在 RegisterNotification
?
Is there a way to predict this order inside RegisterNotification
?
我可以节省 SynchronizationContext.Current
在 RegisterNotification
键,当我打电话以后比较一下 tcs.TrySetResult
。但是,这并不一定意味着等待tcs.Task
将于我救的上下文。
I could save SynchronizationContext.Current
inside RegisterNotification
and compare it later when I call tcs.TrySetResult
. But that not necessarily mean that await tcs.Task
will take place on the context I saved.
在理论上,如果我能predict这一点,我也许可以用它来诊断和prevent潜在的死锁。
In theory, if I could predict this, I might be able to use it to diagnose and prevent potential deadlocks.
推荐答案
我不认为这是一个记录的方式的predict同步/异步行为的setResult
提前。如果您要明确征收异步延续, Task.Run(()=&GT; tcs.SetResult())由@Damien_The_Unbeliever提出
的想法很简单和普遍。
I don't think there is a documented way to predict synchronous/asynchronous behaviour of SetResult
in advance. If you want to explicitly impose asynchronous continuation, the Task.Run(() => tcs.SetResult())
idea proposed by @Damien_The_Unbeliever is simple and universal.
然而,如果你的真正的想减少线程切换,仍然迫使异步性,你可以换 tcs.SetResult
使用自定义哑的SynchronizationContext
。相比于等待tcs.Task
(以及该注册可能在任何其他的延续 TCS的背景下它的唯一目的是它的独特性。任务
)。这将在 TaskCompletionSource
的消费者身边,因为异步调用(S),或者通过 SynchronizationContext.Post
或在游泳池螺纹(如果有关于消费者方面没有同步上下文)。
However, if you really would like to reduce thread switching and still force asynchrony, you could wrap tcs.SetResult
with a custom dumb SynchronizationContext
. The only purpose of it would be its uniqueness as compared to the context of await tcs.Task
(and to that of any other continuations possibly registered on tcs.Task
). That would cause asynchronous continuation(s) on the consumer side of TaskCompletionSource
, either via SynchronizationContext.Post
or on a pool thread (if there's no synchronization context on the consumer side).
测试程序:
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinForms_21845495
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
this.Load += async (s, e) =>
{
// test on WindowsFormsSynchronizationContext
await RequestNotificationAsync(notifyAsync: false);
Debug.WriteLine(String.Empty);
await RequestNotificationAsync(notifyAsync: true);
Debug.WriteLine(String.Empty);
// test on a pool thread
await Task.Run(() => RequestNotificationAsync(notifyAsync: false));
Debug.WriteLine(String.Empty);
await Task.Run(() => RequestNotificationAsync(notifyAsync: true));
Debug.WriteLine(String.Empty);
};
}
async Task RegisterNotification(TaskCompletionSource<object> tcs, bool notifyAsync)
{
await Task.Delay(500);
Debug.WriteLine("A.before");
if (notifyAsync)
{
tcs.SetResultAsync(null);
}
else
{
tcs.SetResult(null);
}
Debug.WriteLine("A.after");
}
async Task RequestNotificationAsync(bool notifyAsync)
{
var tcs = new TaskCompletionSource<object>();
var task = this.RegisterNotification(tcs, notifyAsync);
Debug.WriteLine("B.before");
var data = await tcs.Task;
// do not yeild
Thread.Sleep(500);
Debug.WriteLine("B.after");
// yeild
await Task.Delay(500);
}
}
public static class TaskExt
{
static public void SetResultAsync<T>(this TaskCompletionSource<T> tcs, T result)
{
FakeSynchronizationContext.Execute(() => tcs.SetResult(result));
}
// FakeSynchronizationContext
class FakeSynchronizationContext : SynchronizationContext
{
private static readonly ThreadLocal<FakeSynchronizationContext> s_context =
new ThreadLocal<FakeSynchronizationContext>(() => new FakeSynchronizationContext());
private FakeSynchronizationContext() { }
public static FakeSynchronizationContext Instance { get { return s_context.Value; } }
public static void Execute(Action action)
{
var savedContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(FakeSynchronizationContext.Instance);
try
{
action();
}
finally
{
SynchronizationContext.SetSynchronizationContext(savedContext);
}
}
// SynchronizationContext methods
public override SynchronizationContext CreateCopy()
{
return this;
}
public override void OperationStarted()
{
throw new NotImplementedException("OperationStarted");
}
public override void OperationCompleted()
{
throw new NotImplementedException("OperationCompleted");
}
public override void Post(SendOrPostCallback d, object state)
{
throw new NotImplementedException("Post");
}
public override void Send(SendOrPostCallback d, object state)
{
throw new NotImplementedException("Send");
}
}
}
}
输出:
B.before
A.before
B.after
A.after
B.before
A.before
A.after
B.after
B.before
A.before
B.after
A.after
B.before
A.before
A.after
B.after
这篇关于在TaskCompletionSource.TrySetResult同步或异步延续?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!