STA 线程在 Winforms 中使用,但在作为控制台应用程序执行时不使用
STA thread used in Winforms but not when executed as a Console app
我有一个在第三方沙箱内的 STA 线程下运行的程序集,在这个线程中我创建了一个双工 WCF 客户端,它需要在原始 STA 线程上执行方法。
当前的实现工作正常,在 Duplex 回调中,我按如下方式获取 STA 线程的同步上下文,并将其用于 Post 返回 STA 线程:
private readonly SynchronizationContext _syncContext = AsyncOperationManager.SynchronizationContext;
这一切都在 STA 线程中初始化的 WinForm 中运行,很好...但是我需要移动 WCF 双工代理,以便它在主 STA 线程中的 class 实例下运行。当我删除 winform 时,我最终从上面的 SynchronizationContext 获得了一个全新的线程。
澄清一下:
Winforms:-
- 在 STA 线程上启动 WCF 双工代理 - ManagedThreadId = 1
- 从服务器接收双工回调 - ManagedThreadId = 5
- Post 使用 AsyncOperationManager.SynchronizationContext 回调事件方法 - ManagedThreadId = 1
没有 WinForm(class 实例):-
- 在 STA 线程上启动 WCF 双工代理 - ManagedThreadId = 1
- 从服务器接收双工回调 - ManagedThreadId = 6
- Post 使用 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
的回调(因此 "random" 工作线程将接收它们)。
在 Windows 表单应用程序中,主线程的 SynchronizationContext
将为您初始化为 WindowsFormsSynchronizationContext
实例。该实例将 Post
回调到主 UI 线程。
在 WPF 应用程序中,这将是 DispatcherSynchronizationContext
。
在控制台应用程序中,主线程不会有 SynchronizationContext
。因此,我上面提到的自由线程选项启动了,所以你得到了一个自由线程 SynchronizationContext
实例,它发布到 ThreadPool
。这几乎可以解释您观察到的行为。
如果您需要同步,您可以为控制台应用程序的主线程实现自己的线程仿射 SynchronizationContext
。但这并不容易。在控制台应用程序中,您没有消息循环,也没有可以管理回调队列的调度程序。您可以查看 Stephen Cleary 的 以了解异步 SynchronizationContext
的想法。不过,您需要手动创建 'main loop'。
我有一个在第三方沙箱内的 STA 线程下运行的程序集,在这个线程中我创建了一个双工 WCF 客户端,它需要在原始 STA 线程上执行方法。
当前的实现工作正常,在 Duplex 回调中,我按如下方式获取 STA 线程的同步上下文,并将其用于 Post 返回 STA 线程:
private readonly SynchronizationContext _syncContext = AsyncOperationManager.SynchronizationContext;
这一切都在 STA 线程中初始化的 WinForm 中运行,很好...但是我需要移动 WCF 双工代理,以便它在主 STA 线程中的 class 实例下运行。当我删除 winform 时,我最终从上面的 SynchronizationContext 获得了一个全新的线程。
澄清一下:
Winforms:-
- 在 STA 线程上启动 WCF 双工代理 - ManagedThreadId = 1
- 从服务器接收双工回调 - ManagedThreadId = 5
- Post 使用 AsyncOperationManager.SynchronizationContext 回调事件方法 - ManagedThreadId = 1
没有 WinForm(class 实例):-
- 在 STA 线程上启动 WCF 双工代理 - ManagedThreadId = 1
- 从服务器接收双工回调 - ManagedThreadId = 6
- Post 使用 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
的回调(因此 "random" 工作线程将接收它们)。
在 Windows 表单应用程序中,主线程的 SynchronizationContext
将为您初始化为 WindowsFormsSynchronizationContext
实例。该实例将 Post
回调到主 UI 线程。
在 WPF 应用程序中,这将是 DispatcherSynchronizationContext
。
在控制台应用程序中,主线程不会有 SynchronizationContext
。因此,我上面提到的自由线程选项启动了,所以你得到了一个自由线程 SynchronizationContext
实例,它发布到 ThreadPool
。这几乎可以解释您观察到的行为。
如果您需要同步,您可以为控制台应用程序的主线程实现自己的线程仿射 SynchronizationContext
。但这并不容易。在控制台应用程序中,您没有消息循环,也没有可以管理回调队列的调度程序。您可以查看 Stephen Cleary 的 SynchronizationContext
的想法。不过,您需要手动创建 'main loop'。