BackgroundWorker - Недопустимая операция между потоками - PullRequest
15 голосов
/ 05 января 2012

У меня есть приложение winform (одна форма), в этой форме есть RichTextBox.В конструкторе этой формы я создаю экземпляр класса MyClass.В «Form_Load» я вызываю метод Initialisation из MyClass instance.

В конструкторе формы

myClass = new MyClass(RichTextBox richTextBox);

В Form_Load

myClass.Initialisation();

В методе Initialisation, в цикле, я читаю, что некоторые параметры делают другие вещи.Чтобы не заморозить приложение (поскольку некоторые операции могут занять некоторое время, несколько секунд), я использую BackgroundWorker.Я использую это так (см. Код ниже).

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

Не могли бы вы сказать мне, как решить эту проблему?Работайте идеально, когда у меня нет доступа к richTextBox

public Class MyClass
{
    static BackgroundWorker _bw;
    public MyClass()
    {
        _bw = new BackgroundWorker
        {
            WorkerReportsProgress = true,
            WorkerSupportsCancellation = true
        };
        _bw.DoWork += bw_DoWork;
        _bw.ProgressChanged += bw_ProgressChanged;
        _bw.RunWorkerCompleted += bw_RunWorkerCompleted;
    }
    static void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        foreach (....)
        {
            if (....)
            {
                richtextBox.Text.AppendText("MyText");
            }
        }
        e.Result = true;
    }
    static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){}
    static void bw_ProgressChanged(object sender, ProgressChangedEventArgs e){}
}

Ответы [ 7 ]

32 голосов
/ 05 января 2012

Использование BackgroundWorker не освобождает вас от нормальных правил потоков - например, только поток пользовательского интерфейса может обращаться к компонентам пользовательского интерфейса.

Если вы хотите обновить пользовательский интерфейс из BackgroundWorker , отличного от , используя события прогресса / завершения (которые вызываются в потоке пользовательского интерфейса), вам нужно использовать Control.Invoke / Control.BeginInvoke так же, как и в других ситуациях. Например:

if (....)
{
    Action action = () => richtextBox.Text.Add("MyText");
    richtextBox.Invoke(action); // Or use BeginInvoke
}
12 голосов
/ 05 января 2012

попробуйте этот код,

BeginInvoke((MethodInvoker)delegate
{
    richtextBox.Text.Add("MyText");
});
3 голосов
/ 05 января 2012

Используя компонент BackgroundWorker, только события ProgressChanged и RunWorkerCompleted позволяют вызывать методы / свойства для элементов управления пользовательского интерфейса (что всегда должно выполняться в потоке пользовательского интерфейса). Когда вы обновляете пользовательский интерфейс в событии DoWork, которое выполняется в потоке, не являющемся пользовательским интерфейсом, вы получаете эту ошибку, вам, вероятно, следует обновить ваши элементы управления пользовательским интерфейсом, используя методы Invoke или BeginInvoke в событии DoWork.

1 голос
/ 18 августа 2016

Вы также можете попробовать это.

this.Invoke(new MethodInvoker(delegate()
{
    richtextBox.Text.Add("MyText");
}));
1 голос
/ 23 октября 2013

Чтобы сделать его чище и основываясь на предложении Джона Скита, я сделал метод расширения, который делает то же самое, вы можете изменить "this Label control" на this TextBox control или просто использовать "this Control control" (и в основном разрешать каждыйуправление должно быть легко обновлено):

internal static class ExtensionMethods
{
    /// <summary>
    /// Updates the label text while being used in a multithread app.
    /// </summary>
    /// <param name="control">The control.</param>
    /// <param name="text">The text.</param>
    internal static void UpdateThreadedText(this Label control, string text)
    {
        Action action = () => control.Text = text;
        control.Invoke(action);
    }

    /// <summary>
    /// Refreshes the threaded.
    /// </summary>
    /// <param name="control">The control.</param>
    internal static void RefreshThreaded(this Label control)
    {
        Action action = control.Refresh;
        control.Invoke(action);
    }
}

И тогда использование довольно просто:

this.yourLabelName.UpdateThreadedText("This is the text");
this.yourTextBoxName.UpdateThreadedText("This is the text");
this.yourControlName.UpdateThreadedText("This is the text");

или

this.yourLabelName.RefreshThreaded();

Работает у меня приятно :))

0 голосов
/ 05 января 2012

Добавить:

e.Result = "MyText";

В вашем bw_DoWork И:

richTextBox1.AppendText((string)e.Result);

В вашем bw_RunWorkerCompleted

(измените его под свой код)

РЕДАКТИРОВАТЬ:

Если это было сделано много раз во время работы BackgroundWorker, вы можете добавить:

_bw.ReportProgress(0, "MyText");

к bw_DoWork и:

richTextBox1.AppendText((string)e.UserState);

до bw_ProgressChanged.

0 голосов
/ 05 января 2012

Я думаю, что ошибка останавливается на этой строке:

richtextBox.Text.Add("MyText");

ваш вопрос мне похож на этот:

BackgroundWorker OnWorkCompleted создает исключение перекрестного потока

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...