Почему нельзя получить доступ к компонентам пользовательского интерфейса от фонового работника? - PullRequest
2 голосов
/ 08 апреля 2011

Темы всех ресурсов общего доступа. В этом вся проблема многопоточных операций.

MSDN говорит:

Вы должны быть осторожны, чтобы не манипулировать объектами пользовательского интерфейса в обработчике событий DoWork>. Вместо этого обменивайтесь информацией с пользовательским интерфейсом через события ProgressChanged и RunWorkerCompleted.

События BackgroundWorker не маршалируются через границы AppDomain. Не используйте компонент BackgroundWorker для выполнения многопоточных операций в нескольких доменах приложений.

И все же, когда я использую фонового работника, мне не нужно быть осторожным, чтобы не манипулировать какими-либо объектами пользовательского интерфейса, это не может, если я пытаюсь получить доступ к компонентам пользовательского интерфейса из события DOWork. Код компилируется, но когда запускается код для DoWork, я получаю сообщение об ошибке:

Операция с несколькими потоками недопустима: управление utAlerts осуществляется из потока, отличного от потока, в котором он был создан.

MSDN ничего не говорит о том, как это делается и почему. Фоновый работник украшен каким-то атрибутом, который предотвращает это? Как это достигается?

Ответы [ 3 ]

6 голосов
/ 08 апреля 2011

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

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

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


Редактировать:

BackgroundWorker предназначен для выполнения "работы", не связанной с вашимПользовательский интерфейс.Вы не можете изменять элементы пользовательского интерфейса в любом потоке, кроме потока пользовательского интерфейса, так как элементы пользовательского интерфейса имеют тенденцию иметь сродство к потоку.На самом деле это не имеет ничего общего с BackgroundWorker, а скорее с потоками и элементами пользовательского интерфейса.

BW предназначен для решения этой проблемы, предоставляя вам события прогресса и завершения, которые автоматически перенаправляются обратно в поток пользовательского интерфейса, позволяяВы можете изменить элементы пользовательского интерфейса там.Однако вы всегда можете сделать это непосредственно сами с помощью Control.Invoke в Windows Forms или Dispatcher.Invoke в WPF.

От того, как это работает - зависит от того, какую платформу вы используете.Например, в Windows Forms каждый Control (который является базовым классом всех элементов пользовательского интерфейса) имеет дескриптор, а дескриптор является внутренним дескриптором окна.Этот дескриптор используется для проверки идентификатора потока окна по текущему идентификатору потока.Это позволяет выполнять проверку без сохранения дополнительных переменных.

4 голосов
/ 08 апреля 2011

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

  private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
  {   
        textBox1.Text = "Test";
  }

Results in:

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.

Это используется для того, чтобы несколько потоков не обращались к одним и тем же элементам управления и не меняли их одновременно.BackgroundWorkers являются асинхронными и могут вызвать много проблем, если элементы управления были обновлены, в то время как основной поток также обновляет их.

Я не знаю, как они достигли этого, однако, вероятно, в интересахони предотвратили это.

MSDN предоставил еще одну строку документации для скопированного вами сегмента, в котором говорится, что события BackgroundWorker не маршалируются через границы AppDomain. Не используйте компонент BackgroundWorker для выполнения многопоточных операций более чем в одномAppDomain. "

РЕДАКТИРОВАНИЕ СООТВЕТСТВУЕТ ПРЕОБРАЗОВАНИЮ В КОММЕНТАРИИ:

    private void Form1_Load(object sender, EventArgs e)
    {
        TextBox.CheckForIllegalCrossThreadCalls = false;
        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {   
        textBox1.Text = "Test";
    }

При добавлении CheckForIllegalCrossThreadCalls = false этот код выполняется без ошибок.

В сводкев логическом свойстве указывается, что он указывает, нужно ли «перехватывать вызовы в неправильном потоке».

3 голосов
/ 08 апреля 2011

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

class ThreadAffineObject
{
    private readonly Thread originalThread;
    public ThreadAffineObject()
    {
        this.originalThread = Thread.CurrentThread;
    }

    private void PreventCrossThreadOperation()
    {
        if(Thread.CurrentThread != originalThread)
            throw new CrossThreadOperationException();
    }

    public void DoStuff()
    {
        PreventCrossThreadOperation();
        // Actually do stuff
    }

    private int someField;
    public int SomeProperty
    {
        get { return someField; } // here reading is allowed from other threads
        set
        {
            PreventCrossThreadOperation(); // but writing isn't
            someField = value;
        }
    }
}
...