Ответ 1
Имейте в виду, что очередь сообщений, созданная Windows для потока STA, уже представляет собой реализацию потоковой безопасности. Поэтому просто используйте его для своих целей. Здесь базовый класс, который вы можете использовать, выводит ваш собственный объект COM. Переопределите метод Initialize(), он будет вызываться, как только поток будет готов начать выполнение кода. Не забудьте вызвать base.Initialize() в вашем переопределении.
Вы хотите запустить код в этом потоке, затем используйте методы BeginInvoke или Invoke, как и для методов Control.Begin/Invoke или Dispatcher.Begin/Invoke. Вызовите его метод Dispose(), чтобы отключить поток, он является необязательным. Помните, что это безопасно, только если вы на 100% уверены, что все COM-объекты завершены. Поскольку у вас обычно нет такой гарантии, лучше не делать этого.
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;
}