如何在非 WinForms 程序集中抽取 STA 线程?
How can you pump a STA Thread in a non-WinForms assembly?
Hans 给出了关于泵送 STA 线程的这个很好的答案:
using System;
using System.Threading;
using System.Windows.Forms;
class STAThread : IDisposable {
public STAThread() {
using (mre = new ManualResetEvent(false)) {
thread = new Thread(() => {
Application.Idle += Initialize;
Application.Run();
});
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
mre.WaitOne();
}
}
public void BeginInvoke(Delegate dlg, params Object[] args) {
if (ctx == null) throw new ObjectDisposedException("STAThread");
ctx.Post((_) => dlg.DynamicInvoke(args), null);
}
public object Invoke(Delegate dlg, params Object[] args) {
if (ctx == null) throw new ObjectDisposedException("STAThread");
object result = null;
ctx.Send((_) => result = dlg.DynamicInvoke(args), null);
return result;
}
protected virtual void Initialize(object sender, EventArgs e) {
ctx = SynchronizationContext.Current;
mre.Set();
Application.Idle -= Initialize;
}
public void Dispose() {
if (ctx != null) {
ctx.Send((_) => Application.ExitThread(), null);
ctx = null;
}
}
private Thread thread;
private SynchronizationContext ctx;
private ManualResetEvent mre;
}
但它依赖于像 Application.Run 这样的调用,它是一个 Windows 表单 class,我不认为我想要在非 UI 静态库中。
那么有没有办法调整它,或者它甚至可以按原样使用?
您可以使用 Stephen Cleary 的 Nito.AsyncEx.Context
nuget 中的 AsyncContextThread
。来自其 github description
AsyncContextThread provides properties that can be used to schedule tasks on that thread.
nuget 包含 SynchronizationContext
的自定义实现,因此可以轻松重写代码,例如如:
using System;
using System.Threading;
using Nito.AsyncEx;
class STAThread : IDisposable
{
public STAThread()
{
ctx = new AsyncContextThread();
}
public void BeginInvoke(Delegate dlg, params Object[] args)
{
ctx.Context.SynchronizationContext
.Post((_) => dlg.DynamicInvoke(args), null);
}
public object Invoke(Delegate dlg, params Object[] args)
{
object result = null;
ctx.Context.SynchronizationContext
.Send((_) => result = dlg.DynamicInvoke(args), null);
return result;
}
public void Dispose()
{
ctx.JoinAsync().GetAwaiter().GetResult();
ctx.Dispose();
}
private readonly AsyncContextThread ctx;
}
顺便说一句,从 this MSDN article 开始,并非所有 SynchronizationContext
的实现都保证委托将在特定线程上执行,而 WinForms 和 WPF SynchronizationContext
保证,默认和 ASP.NET 没有。
Hans 给出了关于泵送 STA 线程的这个很好的答案:
using System;
using System.Threading;
using System.Windows.Forms;
class STAThread : IDisposable {
public STAThread() {
using (mre = new ManualResetEvent(false)) {
thread = new Thread(() => {
Application.Idle += Initialize;
Application.Run();
});
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
mre.WaitOne();
}
}
public void BeginInvoke(Delegate dlg, params Object[] args) {
if (ctx == null) throw new ObjectDisposedException("STAThread");
ctx.Post((_) => dlg.DynamicInvoke(args), null);
}
public object Invoke(Delegate dlg, params Object[] args) {
if (ctx == null) throw new ObjectDisposedException("STAThread");
object result = null;
ctx.Send((_) => result = dlg.DynamicInvoke(args), null);
return result;
}
protected virtual void Initialize(object sender, EventArgs e) {
ctx = SynchronizationContext.Current;
mre.Set();
Application.Idle -= Initialize;
}
public void Dispose() {
if (ctx != null) {
ctx.Send((_) => Application.ExitThread(), null);
ctx = null;
}
}
private Thread thread;
private SynchronizationContext ctx;
private ManualResetEvent mre;
}
但它依赖于像 Application.Run 这样的调用,它是一个 Windows 表单 class,我不认为我想要在非 UI 静态库中。
那么有没有办法调整它,或者它甚至可以按原样使用?
您可以使用 Stephen Cleary 的 Nito.AsyncEx.Context
nuget 中的 AsyncContextThread
。来自其 github description
AsyncContextThread provides properties that can be used to schedule tasks on that thread.
nuget 包含 SynchronizationContext
的自定义实现,因此可以轻松重写代码,例如如:
using System;
using System.Threading;
using Nito.AsyncEx;
class STAThread : IDisposable
{
public STAThread()
{
ctx = new AsyncContextThread();
}
public void BeginInvoke(Delegate dlg, params Object[] args)
{
ctx.Context.SynchronizationContext
.Post((_) => dlg.DynamicInvoke(args), null);
}
public object Invoke(Delegate dlg, params Object[] args)
{
object result = null;
ctx.Context.SynchronizationContext
.Send((_) => result = dlg.DynamicInvoke(args), null);
return result;
}
public void Dispose()
{
ctx.JoinAsync().GetAwaiter().GetResult();
ctx.Dispose();
}
private readonly AsyncContextThread ctx;
}
顺便说一句,从 this MSDN article 开始,并非所有 SynchronizationContext
的实现都保证委托将在特定线程上执行,而 WinForms 和 WPF SynchronizationContext
保证,默认和 ASP.NET 没有。