如何将后台工作线程设置为单线程单元?

Kal*_*exx 26 c# clipboard exception-handling backgroundworker sta

我正在创建一个自动测试运行应用程序.在这部分应用程序中,我正在研究一个轮询服务器.它的工作原理是不断轮询Web服务器,以确定何时应运行新的自动化测试(对于我们的GUI应用程序的夜间自动运行).

当轮询服务器看到请求时,它会下载所需的所有信息,然后在后台工作程序中执行测试运行.问题是测试运行的一部分具有Clipboard.Clear()在后台工作线程中发生的OLE,COM和其他调用(例如).发生其中一个调用时,会发生以下异常:

在进行OLE调用之前,必须将当前线程设置为单线程单元(STA)模式.确保您的Main函数标记了STAThreadAttribute.

如何将后台工作线程标记为单线程单元?我的Program.cs中的Main调用显然已经具有该属性.

Han*_*ant 36

这是不可能的,BGW使用线程池线程.TP线程始终是MTA,不能更改.您必须使用常规Thread,在启动之前调用SetApartmentState().这个线程也应该输出一个消息循环,调用Application.Run().

也许您应该考虑从UI线程调用此代码.因为很可能COM服务器无论如何都在UI线程上运行它的方法.从工作线程到创建COM服务器的STA线程的Marshaling调用是自动的,COM负责处理它.

或者采取公牛角和自己编组.您可以创建自己的STA线程,为服务器提供一个幸福的家.你会在这篇文章中找到代码,一定要在Initialize()覆盖中创建COM对象.


Sim*_*ier 8

BackgroundWorker默认使用ThreadPool线程,但您可以覆盖此行为.首先,您需要定义自定义SynchronizationContext:

public class MySynchronizationContext : SynchronizationContext
{
    public override void Post(SendOrPostCallback d, object state)
    {
        Thread t = new Thread(d.Invoke);
        t.SetApartmentState(ApartmentState.STA);
        t.Start(state);
    }
}
Run Code Online (Sandbox Code Playgroud)

在使用BackgroundWorker之前,覆盖默认的SynchronizationContext,如下所示:

   AsyncOperationManager.SynchronizationContext = new MySynchronizationContext();
Run Code Online (Sandbox Code Playgroud)

注意:这可能会对应用程序的其余部分产生性能影响,因此您可能希望限制新的Post实现(例如使用stated参数).


Con*_*Wet 7

我还没有测试过它,但是如果你调用 WinForms 表单,你应该回到 UI 线程并且大多数东西应该再次工作。

BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(this.bgw_DoWork);
bgw.RunWorkerAsync();

private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
    // Invoke the UI thread
    // "this" is referring to the Form1, or what ever your form is
    this.Invoke((MethodInvoker)delegate
    {
        Clipboard.GetText();
        // etc etc
    });
}
Run Code Online (Sandbox Code Playgroud)