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

发布于 2024-10-12 02:16:11 字数 421 浏览 9 评论 0原文

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

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

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

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

I am creating an automated test running application. In this part of the application, I am working on a polling server. It works by constantly polling the web server to determine when a new automated test should be run (for nightly automated runs of our GUI application).

When the polling server sees a request, it downloads all the information necessary and then executes the test run in a background worker. The problem is that part of the test run has OLE, COM, and other calls (for example, Clipboard.Clear()) that occur in the background worker thread. When one of these calls occurs, the following exception occurs:

Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it.

How can I mark a background worker thread as single thread apartment? The Main call in my Program.cs obviously already has that attribute.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

孤独患者 2024-10-19 02:16:11

这是不可能的,BGW使用的是threadpool线程。 TP 线程始终是 MTA,无法更改。您必须使用常规线程,在启动之前调用 SetApartmentState()。该线程还应该泵送消息循环,调用Application.Run()。

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

或者不畏艰险,亲自指挥。你可以创建自己的STA线程来给服务器一个幸福的家。您将在这篇文章中找到代码,请务必在 Initialize() 重写中创建 COM 对象。

This is not possible, BGW uses a threadpool thread. TP threads are always MTA, it cannot be changed. You will have to use a regular Thread, call SetApartmentState() before you start it. This thread also should pump a message loop, call Application.Run().

Maybe you ought to consider calling this code from the UI thread. Because in all likelihood, the COM server is running its methods on the UI thread anyway. Marshaling calls from a worker thread to the STA thread that created the COM server is automatic, COM takes care of it.

Or take the bull by the horns and marshal yourself. You can create your own STA thread to give the server a happy home. You'll find code in this post, be sure to create the COM object in your Initialize() override.

嘿看小鸭子会跑 2024-10-19 02:16:11

默认情况下,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);
    }
}

并覆盖默认的 SynchronizationContext,如下所示,然后再进行使用您的BackgroundWorker:

   AsyncOperationManager.SynchronizationContext = new MySynchronizationContext();

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

BackgroundWorker uses by default a ThreadPool thread, but you can override this behavior. First you need to define a custom 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);
    }
}

And override the default SynchronizationContext, like this, before you use your BackgroundWorker:

   AsyncOperationManager.SynchronizationContext = new MySynchronizationContext();

NOTE: this can have performance effects on the rest of your application, so you might want to restrict the new Post implementation (for example using the state or d parameters).

赠我空喜 2024-10-19 02:16:11

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

I have not tested it, but if you invoke the WinForms Form, you should be back to the UI thread and most of the stuff should work again.

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
    });
}
呆° 2024-10-19 02:16:11

通常,您可以通过在入口点(例如 Static Main)上定义属性 [STAThread()] 来设置它。

You normally set it by defining attributre [STAThread()] on the entry point (e.g. Static Main).

猫卆 2024-10-19 02:16:11

我使用了 +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
        });
    }

I used +Conrad de Wet's idea and it worked great!

There is one small issue with that code though, you have to close the "this.Invoke....." like with a });

Here is Conrad de Wet's code with this fix:

    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
        });
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文