Post异步投递立即返回,Send同步阻塞直至目标上下文执行完成;Post适用于避免卡住调用方,Send仅在必须确保逻辑执行完再继续时使用,且现代.NET中多数上下文不支持Send。

Post 是异步投递,Send 是同步调用
Post 把委托排队到目标上下文(比如 UI 线程),立即返回,不等执行;Send 会阻塞当前线程,直到委托在目标上下文执行完才返回。这决定了它们的适用场景完全不同:需要避免卡住调用方时用 Post,必须确保某段逻辑在目标上下文完成后再继续时才考虑 Send。
-
Post在 WinForms 中最终走Control.BeginInvoke,WPF 中走Dispatcher.BeginInvoke -
Send在 WinForms 中对应Control.Invoke,WPF 中是Dispatcher.Invoke,但注意:.NET Core / .NET 5+ 的默认SynchronizationContext(如ThreadPoolSynchronizationContext)根本不支持Send,调用会直接抛NotSupportedException - 在 ASP.NET Core 或控制台应用中,若没显式安装自定义上下文(如
AsyncLocalSynchronizationContext),Current通常为null,此时调用Post或Send都会触发NullReferenceException
Send 可能引发死锁,Post 一般不会
典型死锁场景:UI 线程调用 await Task.Run(() => { context.Send(...); }),而 Send 又试图在 UI 线程执行——此时 UI 线程正被 await 卡住,无法处理 Send 请求,形成循环等待。而 Post 不阻塞,只发消息就返回,不会卡住调用线程,因此天然规避这类问题。
- WinForms/WPF 中,从 UI 线程调用
Send到自身上下文是安全的(即同一线程同步调用),但跨线程调用Send风险极高 - 即使在支持
Send的上下文中,也应优先用Post+TaskCompletionSource模拟同步语义,而不是直接用Send - 某些自定义
SynchronizationContext(如旧版 ASP.NET 的AspNetSynchronizationContext)重写了Send,使其退化为Post+ 自旋等待,这种“伪同步”仍可能耗尽线程池资源
Post 的委托执行时机不可控,Send 的执行时机相对确定
Post 提交的任务会被放入目标上下文的消息队列尾部,需等前面所有待处理消息(包括输入事件、计时器回调等)执行完才轮到它;Send 虽然也排队,但在多数 UI 上下文中会尝试“插队”或尽快调度(例如 WPF 的 Dispatcher.Invoke 默认用 Normal 优先级,但可指定 Send 级别)。
- WinForms 中
Post等价于BeginInvoke,任务进入 Windows 消息循环的QS_POSTMESSAGE队列,受系统消息调度影响 - WPF 中
Post对应Dispatcher.BeginInvoke,优先级固定为Background,低于输入/渲染等关键任务 - 如果需要比默认
Post更快响应,WPF 可改用Dispatcher.InvokeAsync并传入DispatcherPriority.Loaded或更高,但这已脱离SynchronizationContext接口范畴
var context = SynchronizationContext.Current;
if (context == null)
{
// 当前无上下文,不能 Post/Send
Console.WriteLine("No SynchronizationContext available");
}
else
{
context.Post(_ => Console.WriteLine("This runs asynchronously on target context"), null);
// 下一行立即执行,不等上面那句输出
try
{
context.Send(_ => Console.WriteLine("This blocks until done"), null);
// 这行执行完,上面那句才一定已输出
}
catch (NotSupportedException ex)
{
// .NET Core 默认上下文不支持 Send
Console.WriteLine($"Send not supported: {ex.Message}");
}
}
真正容易被忽略的是:很多开发者以为只要 SynchronizationContext.Current != null 就能安全调用 Send,实际上还得看具体实现是否允许——而现代 .NET 应用里,这个“允许”的情况越来越少了。









