Как сделать фоновый рабочий поток установленным на Однопотоковую квартиру? - PullRequest
26 голосов
/ 14 января 2011

Я создаю приложение для автоматического тестирования.В этой части приложения я работаю на сервере опросов.Он работает, постоянно опрашивая веб-сервер, чтобы определить, когда следует запускать новый автоматический тест (для ночных автоматических запусков нашего приложения с графическим интерфейсом).

Когда сервер опроса видит запрос, он загружает всю необходимую информацию изатем выполняет тестовый запуск в фоновом режиме.Проблема состоит в том, что часть тестового прогона имеет OLE, COM и другие вызовы (например, Clipboard.Clear()), которые происходят в фоновом рабочем потоке.Когда происходит один из этих вызовов, возникает следующее исключение:

Для выполнения OLE-вызовов текущий поток должен быть переведен в однопотоковый режим (STA).Убедитесь, что в вашей функции Main помечен атрибут STAThreadAttribute.

Как пометить фоновый рабочий поток как однопотоковую квартиру?Вызов Main в моем Program.cs, очевидно, уже имеет этот атрибут.

Ответы [ 5 ]

36 голосов
/ 14 января 2011

Это невозможно, BGW использует поток пула потоков. Потоки ТП всегда МТА, их нельзя изменить. Вам придется использовать обычный поток, вызвать SetApartmentState (), прежде чем запустить его. Этот поток также должен прокачать цикл сообщений, вызвать Application.Run ().

Возможно, вам следует подумать о вызове этого кода из потока пользовательского интерфейса. Потому что, по всей вероятности, COM-сервер в любом случае выполняет свои методы в потоке пользовательского интерфейса. Маршалинг вызовов из рабочего потока в поток STA, который создал COM-сервер, является автоматическим, COM позаботится об этом.

Или возьми быка за рога и соберись. Вы можете создать свой собственный поток STA, чтобы дать серверу счастливый дом. Вы найдете код в этой записи , обязательно создайте COM-объект в переопределении Initialize ().

8 голосов
/ 14 февраля 2013

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 (например, используя параметры state или d ).

4 голосов
/ 12 августа 2013

Я не проверял это, но если вы вызываете форму WinForms, вы должны вернуться к потоку пользовательского интерфейса, и большая часть материала должна работать снова.

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
    });
}
1 голос
/ 14 января 2011

Обычно вы устанавливаете его, определяя атрибут [STAThread()] в точке входа (например, Static Main).

0 голосов
/ 08 июля 2014

Я использовал + идею Конрада де Вета, и она отлично сработала!

Есть одна небольшая проблема с этим кодом, хотя вы должны закрыть «this.Invoke .....» как с помощью});

Вот код Конрада де Вета с этим исправлением:

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