我怎样才能使后台工作线程设置为单线程公寓?

我正在创build一个自动化testing运行应用程序 在这部分应用程序中,我正在使用一个轮询服务器。 它通过不断轮询Web服务器来确定何时应该运行一个新的自动化testing(对于我们的GUI应用程序的夜间自动运行)。

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

当前的线程必须设置为单线程单元(STA)模式才能进行OLE调用。 确保你的Main函数有标记的STAThreadAttribute。

我怎样才能标记一个后台工作线程作为单线程的公寓? 我的Program.cs中的Main调用显然已经有了这个属性。

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

也许你应该考虑从UI线程调用这个代码。 因为很可能,COM服务器总是在UI线程上运行它的方法。 将工作线程调用到创buildCOM服务器的STA线程是自动的,COM负责处理它。

或者把牛犊拿过来,自己整理一下。 你可以创build你自己的STA线程来给服务器一个快乐的家。 你会发现在这篇文章中的代码,一定要在你的Initialize()覆盖中创buildCOM对象。

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实现(例如使用stated参数)。

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

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

我用+ Conrad de Wet的想法,它工作得很好!

这个代码有一个小问题,但你必须closures“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 }); }