- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
如何判断 TaskCompletionSource.TrySetResult
发起的延续是同步执行还是异步执行?
例如:
// 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
在与发生 await tcs.Task
的同步上下文不同的线程上触发,则调试输出将是:
B.beforeA.beforeA.afterB.after
也就是说,await tcs.Task
延续是异步执行的。如果它在同一个同步上下文中触发(或者如果两个地方都没有同步上下文),输出将是:
B.beforeA.beforeB.afterA.after
即continuation是同步执行的。
有没有办法在 RegisterNotification
中预测这个顺序?
我可以将 SynchronizationContext.Current
保存在 RegisterNotification
中,稍后在调用 tcs.TrySetResult
时进行比较。但这并不一定意味着 await tcs.Task
将在我保存的上下文中发生。
理论上,如果我能预测到这一点,我也许可以用它来诊断和防止潜在的死锁。
最佳答案
我认为没有记录在案的方法来提前预测 SetResult
的同步/异步行为。如果你想显式地强加异步延续,@Damien_The_Unbeliever 提出的 Task.Run(() => tcs.SetResult())
想法简单而通用。
但是,如果您真的想减少线程切换并仍然强制异步,您可以用自定义的 SynchronizationContext
包装 tcs.SetResult
>。与 await tcs.Task
的上下文(以及可能在 tcs.Task
上注册的任何其他延续的上下文)相比,它的唯一目的是它的唯一性。这将导致 TaskCompletionSource
的消费者端异步继续,通过 SynchronizationContext.Post
或池线程(如果消费者端没有同步上下文) .
测试应用:
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.beforeA.beforeB.afterA.afterB.beforeA.beforeA.afterB.afterB.beforeA.beforeB.afterA.afterB.beforeA.beforeA.afterB.after
关于c# - TaskCompletionSource.TrySetResult 上的同步或异步继续?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21845495/
如何判断 TaskCompletionSource.TrySetResult 发起的延续是同步执行还是异步执行? 例如: // class A void RegisterNotification(Ta
这是 another SO question 的后续问题关于在异步回调函数上使用异步包装器。 这是目前的代码(@Servy 提供的优秀解决方案): static Task> GetMyDataAsyn
我有一个使用 TaskCompletionSource 包装回调的方法,如下所示: public Task TestMethod(int argument) { var tcs = new T
我正在努力思考 TPL、C# 5 中新的 async/await 特性,以及 TaskCompletionSource. 我不清楚的一件事是何时使用 SetResult、SetException 和
我是一名优秀的程序员,十分优秀!