.NET BackGroundWorker - InvalidOperationException: операция с несколькими потоками недопустима - PullRequest
4 голосов
/ 01 ноября 2011

У меня есть проект, закодированный в .NET Winforms. Мне нужно реализовать операцию интеллектуального анализа данных, распечатать текст в TextBox и обновить прогресс.

Я пытался использовать BackgroundWorker, но он выдает InvalidOperationException ( Операция с несколькими потоками недопустима: доступ к элементу управления xxxxx осуществляется из потока, отличного от потока, который был создан в )

Чтобы сузить потенциальные причины проблемы, я начал новый проект, включающий следующее: Кнопка - чтобы запустить BackgroundWorker Метка - для печати текста. И ПрогрессБар.

Однако результат тот же. Я искал на SOF, и мне сказали использовать делегата, но я не знаком с ним.

Это пример кода, который выдает ошибку:

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace TestProject
{
    public partial class Form1 : Form
    {
        private readonly BackgroundWorker _bw = new BackgroundWorker();

        public Form1()
        {
            InitializeComponent();
            _bw.DoWork += RosterWork;
            _bw.ProgressChanged += BwProgressChanged;
            _bw.RunWorkerCompleted += BwRunWorkerCompleted;
            _bw.WorkerReportsProgress = true;
            _bw.WorkerSupportsCancellation = false;
        }

        private void RosterWork(object sender, DoWorkEventArgs doWorkEventArgs)
        {
            for (int i = 0; i < 1000; i++)
            {
                label1.Text = i.ToString();
                _bw.ReportProgress(Convert.ToInt32((i * (100 / 1000))));
            }
        }

        private void BwProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            progressBar1.Show();
            _bw.RunWorkerAsync();
        }

        private void BwRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            progressBar1.Hide();
        }

    }
}

Обновление: Я следую ответу Джона Скита, он действительно работает над моим тестовым проектом, но вернемся к моему реальному проекту,

Макет моей формы:

Форма - TabControl - Tab1 -Tab1Panel -TextBox1

Когда достигните этой линии:

TextBox txtbox1 = new TextBox();
Tab1Panel.Controls.Add(txtbox1);

Ошибка по-прежнему возникает при программном добавлении текстового поля в панель управления.

Наконец, я заменяю на это:

 if (Tab1Panel.InvokeRequired)
     Tab1Panel.Invoke((MethodInvoker)delegate { Tab1Panel.Controls.Add(txtbox1); });
 else
     Tab1Panel.Controls.Add(txtbox1);

Все работает. Как определить, является ли элемент управления InvokeRequired, указан ли этот элемент управления?

Ответы [ 4 ]

11 голосов
/ 01 ноября 2011

Это проблема:

label1.Text = i.ToString();

Вы пытаетесь изменить текст метки в BackgroundWorker, который не выполняется в потоке пользовательского интерфейса.Смысл BackgroundWorker заключается в том, чтобы выполнять всю работу, не связанную с пользовательским интерфейсом, используя ReportProgress, чтобы периодически «возвращаться» к потоку пользовательского интерфейса и обновлять пользовательский интерфейс в соответствии с достигнутым вами прогрессом.

Итак либо вам также нужно изменить label1.Text в BwProgressChanged, или вам нужно использовать Control.Invoke / BeginInvoke так же, как и в любом другом фоновом потоке:

// Don't capture a loop variable in a lambda expression...
int copy = i;
Action updateLabel = () => label1.Text = copy.ToString();
label1.BeginInvoke(updateLabel);

Подробнее о копировании см. В блоге Эрика Липперта, «Закрытие переменной цикла, считающейся вредной» .В данном конкретном случае это только проблема, потому что я использую BeginInvoke.Это можно изменить на просто:

Action updateLabel = () => label1.Text = i.ToString();
label1.Invoke(updateLabel);

... но теперь фоновый работник всегда будет ждать, пока пользовательский интерфейс догонит его, прежде чем он продолжит работу, что в реальной жизни обычно не то, что вы хотите.Я вообще предпочитаю BeginInvoke над Invoke.

4 голосов
/ 27 декабря 2013

используйте этот код, я буду работать

 BeginInvoke((MethodInvoker)delegate
               {
                   TextBox1.Text += "your text here";

               });
0 голосов
/ 01 ноября 2011

Вы должны выполнить методы элемента управления из потока, который их создал.Чтобы обновить их из другого потока, используйте Invoke .Пример того, как это можно сделать, можно найти здесь .Вы также должны прочитать Многопоточность с формами и элементами управления в MSDN.

0 голосов
/ 01 ноября 2011

Вы получаете доступ к своей метке - которая была создана в потоке GUI - из вашего потока backgroundworker.Доступ к элементу управления Windows из потока, отличного от того, в котором он был создан, запрещен;поэтому вы получили исключение.

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

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