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

标签 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 线程中保持双工回调方法的执行?

最佳答案

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

这意味着,获得的 SynchronizationContext 取决于您访问该属性的环境:

  • 您正在访问该属性的线程,以及
  • 应用程序的类型。

如您所见in the docs :

The default implementation is the free-threaded implementation.

因此,如果未设置当前线程的同步上下文,您将获得一个默认的自由线程 SynchronizationContext 实例。它将通过在调用者线程上同步执行来Send 回调并将Post 回调发送到ThreadPool(因此“随机”工作线程将拾取它们)。

在 Windows 窗体应用程序中,主线程的 SynchronizationContext 将被初始化为一个 WindowsFormsSynchronizationContext 实例。该实例会将回调发布到主 UI 线程。

在 WPF 应用程序中,这将是一个 DispatcherSynchronizationContext

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

如果您需要同步,您可以为您的控制台应用程序的主线程实现您自己的线程仿射 SynchronizationContext。但这并不容易。在控制台应用程序中,您没有消息循环,也没有可以管理回调队列的调度程序。你可以看看这个great answer由 Stephen Cleary 提出的异步 SynchronizationContext 的想法。不过,您需要手动创建一个“主循环”。

关于c# - STA 线程在 Winforms 中使用,但在作为控制台应用程序执行时不使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55186575/

相关文章:

c# - 当对象放在 SurfaceListBox 和 ScatterView 上时获取 SurfaceListBoxItem 和 ScatterViewItem

c# - Blazor 组件需要修改父组件的值

c# - 我的计时器事件崩溃,因为事件是在不同的线程上调用的

c# - DefaultView.RowFilter 不适用于日期

c# - 如何在不下载所有页面源的情况下始终获取网站标题

asp.net - 如何在frm(winForms)中创建PasswordHash asp身份o生成PasswordHash

c# - 如何使用 backgroundworker 更新 GUI?

c# - 即使我没有使用 MySQL,ASP.NET 也找不到 MySQL 主机

objective-c - 方法困惑的线程安全

c - 超线程代码示例