Почему я не получаю сообщение об ошибке «Операция с несколькими потоками недействительна» - PullRequest
5 голосов
/ 18 марта 2012

Я использую BackgroundWorker и делаю это:

private void loadNewAsyncToolStripMenuItem_Click(object sender, EventArgs e)
{
    this.Text = "RunWorkerAsync()";
    backgroundWorkerLoading.RunWorkerAsync();
}
private void backgroundWorkerLoading_DoWork(object sender, DoWorkEventArgs e)
{
    UnsafeThreadMethod("hello");
    EvenUnsaferThreadMethod();
}

А теперь два метода.

private void UnsafeThreadMethod(string text)
{
    toolStripLabelRssFeedData.Text = text;
}

private void EvenUnsaferThreadMethod()
{
    panelLoading.Visible = true;
}

Я не понимаю, почему UnsafeThreadMethod не выдает следующее исключение, а EvenUnsaferThreadMethod делает.

Операция между потоками недопустима: элемент управления 'panelLoading' доступен из потока, отличного от> потока, в котором он был создан.

Согласно сообщению, это потому, что toolStripLabelRssFeedData был создан в том же потоке, но это не так.

Я подумал, что не могу вызвать элементы управления, созданные основным потоком, и должен использовать событие ProgressChanged. Что происходит?

И у меня второй вопрос. В чем преимущество этого, когда я могу использовать ProgressChanged? Что мне делать?

private void EvenUnsaferThreadMethod()
{
    if (panelLoading.InvokeRequired)
    {
        panelLoading.Invoke(new MethodInvoker(() => { EvenUnsaferThreadMethod(); }));
    }
    else
    {
        panelLoading.Visible = true;
    }
}

Ответы [ 3 ]

5 голосов
/ 18 марта 2012

На первый вопрос:

  • в режиме отладки намеренно генерируется исключение между потоками. Это означает, что (InvokeRequired) встроена (условная) проверка кода, встроенная в большинство элементов управления GUI. Понравилась панель.

  • Очевидно, ToolstripLabel не выполняет эту проверку. Так как это не вытекает из Контроля, это может быть потому, что оно выходит за рамки этой сети безопасности.

Поскольку стандартный отказ от ответственности «Любые члены экземпляра не гарантированно являются потокобезопасными» относится к ToolstripLabel, я бы просто использовал обычную логику InvokeRequired при установке текста.

3 голосов
/ 18 марта 2012

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

Однако я могу ответить на второй вопрос прямо сейчас. Событие ProgressChanged предназначено именно для того, как оно звучит. Предполагается, что он вызывается, чтобы поток пользовательского интерфейса узнал о состоянии фонового работника, чтобы он мог соответствующим образом обновлять себя. Исходный вызывающий поток (в данном случае пользовательский интерфейс) - это тот, который используется для ProgressChanged, поэтому при обновлении ему не нужно вызывать Invoke. Но на самом деле это нужно делать только для демонстрации прогресса фонового работника.

Теперь, если это не обновление, которое вы пытаетесь передать вызывающему методу, я бы предложил просто передать ваши возвращаемые данные обратно через событие RunWorkerCompleted. Это передает все ваши окончательные данные обратно в исходный поток (UI), так что он может обновлять интерфейс без необходимости Invoke.

Так что, да, ваш звонок на Invoke будет работать, хотя. Однако понимание того, для чего предназначено каждое из других событий, может помочь вам понять, почему использовать один путь над другим. Может быть, ProgressChanged событие подходит лучше? Он также может избавить ваш код от ненужных вызовов.

Обновление до первого q

Я все еще не могу найти что-нибудь о панели инструментов, не нуждающейся в вызове. На самом деле, я нахожу противоположное с помощью поисков Google, таких как «toolstriplabel без перекрестного потока исключений» или «toolstriplabel invoke» и т. Д. Однако, как упоминал Хенк, toolstriplabel не наследуется от контроля, что может объяснить, почему не требуется никакого вызова. Однако я предлагаю предположить, что он будет действовать как любой другой элемент управления пользовательского интерфейса, и убедиться, что он обновлен в потоке пользовательского интерфейса, чтобы быть безопасным. не полагайтесь на причуды . Лучше быть в безопасности, чем потом сожалеть, вы никогда не знаете, могут ли такие вещи измениться, тем более что это логически элемент пользовательского интерфейса для большинства ..,

0 голосов
/ 18 марта 2012

Преимущество вашего второго выбора в том, что он работает :)

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

По этой причине ваш первый отказывает, и именно по этой причине ваш второй случай будет работать. Invoke()... перенаправит требуемый вызов Merhod в основной поток пользовательского интерфейса.

Надеюсь, это поможет.

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