我使用SynchronizationContext
作为同步到GUI线程的方法,用于WinForms和WPF。最近我遇到了旧式异步回调问题:
private void Button_Click(object sender, RoutedEventArgs e)
{
uiContext = SynchronizationContext.Current;
var cl = new TcpClient();
cl.BeginConnect("127.0.0.1", 22222, ConnectedCallback, null);
}
public void ConnectedCallback(IAsyncResult result)
{
if (SynchronizationContext.Current != uiContext)
uiContext.Post(x => MyUIOperation(), null);
else
MyUIOperation();
}
public void MyUIOperation()
{
Title = "Connected";
}
private SynchronizationContext uiContext;
这将引发异常,因为回调函数中的SynchronizationContext.Current
等于捕获的函数,因此UI操作在回调的工作线程中执行。
在WinForms中使用这个完全相同的代码可以像我预期的那样工作。
现在作为一种解决方法,我正在捕获当前的ManagedThreadId
,并在回调中对它进行比较。处理这个问题的正确方法是什么?
更新
我应该补充一点,我正在修改一个当前使用以下构造的非常旧的现有类:
if (control.InvokeRequired())
control.BeginInvoke(SomeFunction);
else
SomeFunction();
我试图删除WinForms依赖项,而不会对此类的客户端产生太大影响。 SomeFunction()
正在引发事件,所以如果我只调用uiContext.Send()或uiContext.Post(),则执行顺序会发生变化,因为Post()将始终对调用进行排队,而Send()将始终阻止。
此外,这只是显示我的问题根源的一小段代码。实际上,可以从主线程调用执行Post()的函数。
这是针对.NET 4.0
答案 0 :(得分:3)
事实证明,在.NET 4.5中,SynchronizationContext
实际上在回调函数中是不同的,if语句将评估为true。正如所讨论的here
WPF 4.0 had a performance optimization where it would frequently reuse the same instance of the DispatcherSynchronizationContext when preparing the ExecutionContext for invoking a DispatcherOperation. This had observable impacts on behavior. 1) Some task-parallel implementations check the reference equality of the SynchronizationContext to determine if the completion can be inlined - a significant performance win. 2) But, the ExecutionContext would flow the SynchronizationContext which could result in the same instance of the DispatcherSynchronizationContext being the current SynchronizationContext on two different threads. The continuations would then be inlined, resulting in code running on the wrong thread.
答案 1 :(得分:2)
因为在我的情况下,需要调用MyUIOperation()函数 如果调用ConnectedCallback函数,则立即调用 主线。
这意味着如果在UI线程中调用MyUIOperation()
,则对ConnectedCallback
的调用将是阻塞调用,而如果从另一个线程调用它则是非阻塞调用。这种非决定论可能导致其他问题。
只需拨打Send
即可。根据{{3}},对Send
的调用只会在UI线程中直接调用该委托。
此外,您可以改为this article。