Winforms中使用的STA线程,但作为控制台应用程序执行时不使用

时间:2019-03-15 16:06:16

标签: c# multithreading winforms wcf callback

我有一个程序集,该程序集在第三方的沙箱中的STA线程下运行,在该线程中,我创建了一个双工WCF客户端,该客户端需要在原始STA线程上执行方法。

当前的实现工作正常,在Duplex回调中,我按如下方式获取STA线程的同步上下文,并将其用于回发到STA线程:

private readonly SynchronizationContext _syncContext = AsyncOperationManager.SynchronizationContext;

所有这些都在STA线程中初始化的WinForm中运行,太好了...但是我需要移动WCF双工代理,因此它可以在主STA线程中的类实例下运行。当我删除winform时,我最终从上述SynchronizationContext中获得了一个全新的线程。

要澄清:

Winforms:-

  • 在STA线程上启动WCF双工代理-ManagedThreadId = 1
  • 从服务器接收双工回调-ManagedThreadId = 5
  • 使用AsyncOperationManager.SynchronizationContext发布到回调事件方法-ManagedThreadId = 1

没有WinForm(类实例):-

  • 在STA线程上启动WCF双工代理-ManagedThreadId = 1
  • 从服务器接收双工回调-ManagedThreadId = 6
  • 使用AsyncOperationManager.SynchronizationContext发布到回调事件方法-ManagedThreadId = 11

在线程11而不是1上执行意味着我的方法无法在沙箱中正确执行,除了在Winform下运行之外,变量之间的代码没有区别。有谁知道我如何在不使用winform的情况下保持STA主线程中双工回调方法的执行?

1 个答案:

答案 0 :(得分:5)

您正在使用AsyncOperationManager.SynchronizationContext属性获取同步上下文。该属性在内部使用SynchronizationContext.Current

这意味着,获得的SynchronizationContext取决于您在以下位置访问属性的环境:

  • 您要访问其属性的线程,以及
  • 应用程序的类型。

您可以阅读in the docs

  

默认实现是自由线程实现。

因此,如果未设置当前线程的同步上下文,则将获得默认的自由线程SynchronizationContext实例。它将通过在调用者线程上同步执行而Send个回调,以及对Post的{​​{1}}个回调(因此,“随机”工作线程会选择它们)。

在Windows Forms应用程序中,主线程的ThreadPool将为您初始化为SynchronizationContext实例。该实例将WindowsFormsSynchronizationContext回调到主UI线程。

在WPF应用中,这将是Post

在控制台应用程序中,主线程没有DispatcherSynchronizationContext。因此,我上面提到的自由线程选项开始执行,因此您获得了一个发布到SynchronizationContext的自由线程SynchronizationContext实例。几乎可以解释您观察到的行为。

如果需要同步,则可以为控制台应用程序的主线程实现自己的线程仿射ThreadPool。不过,这并不容易。在控制台应用程序中,您没有消息循环,也没有可以管理回调队列的调度程序。您可以查看Stephen Cleary的great answer,了解异步SynchronizationContext的概念。不过,您将需要手动创建“主循环”。