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

时间:2011-01-13 21:04:10

标签: c# exception-handling backgroundworker clipboard sta

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

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

  

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

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

5 个答案:

答案 0 :(得分:36)

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

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

或者采取公牛角,并自己编组。您可以创建自己的STA线程,为服务器提供一个幸福的家。您将在this post中找到代码,请确保在Initialize()覆盖中创建COM对象。

答案 1 :(得分: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);
    }
}

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

   AsyncOperationManager.SynchronizationContext = new MySynchronizationContext();

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

答案 2 :(得分:4)

我还没有测试过,但是如果你调用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
    });
}

答案 3 :(得分:1)

您通常是通过在入口点定义attributre [STAThread()]来设置它(例如Static Main)。

答案 4 :(得分:-1)

我使用了+ Conrad de Wet的想法并且效果很好!

但该代码存在一个小问题,你必须关闭“this.Invoke .....”,如同}};

以下是Conrad de Wet的修复代码:

    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
        });
    }