为什么我没有得到“跨线程操作无效”的错误

时间:2022-09-14 11:00:52

I use a BackgroundWorker and do this:

我利用一个背景工作者来做:

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

And now the two methods.

现在有两种方法。

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

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

I don't understand why UnsafeThreadMethod doesn't throw the following exception but EvenUnsaferThreadMethod does.

我不明白为什么UnsafeThreadMethod没有抛出以下异常,但EvenUnsaferThreadMethod却有。

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

交叉线程操作无效:控制从创建它的>线程以外的线程访问的“panelLoading”。

According to the message it's because toolStripLabelRssFeedData was created on the same thread but it wasn't.

根据消息,这是因为toolStripLabelRssFeedData是在同一个线程上创建的,但它不是。

I thought that I can't call controls created by the main thread and have to use the ProgressChanged event. What's going on?

我认为我不能调用主线程创建的控件,必须使用ProgressChanged事件。这是怎么呢

And I have a second question. What is the advantage of doing it like this when I can use ProgressChanged? What should I do?

我还有第二个问题。当我可以使用ProgressChanged时,这样做有什么好处?我应该做什么?

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

3 个解决方案

#1


5  

To the first question:

第一个问题:

  • the cross-thread exception is deliberately thrown in Debug mode. This means there is (conditional) code checking on InvokeRequired built into most of the GUI controls. Like the Panel.

    在调试模式下故意抛出交叉线程异常。这意味着在大多数GUI控件中内置的InvokeRequired(条件)代码检查。像面板。

  • Apparently the ToolstripLabel does not make this check. Since it does not derive from Control that could be because it is outside the scope of this safety net.

    显然工具条标签没有做这个检查。因为它不是来自控制,这可能是因为它超出了这个安全网的范围。

Since the standard disclaimer "Any instance members are not guaranteed to be thread safe" applies to the ToolstripLabel I would just go with the normal InvokeRequired logic when setting the Text.

由于标准的免责声明“任何实例成员都不能保证是线程安全的”适用于工具标记,所以在设置文本时,我将使用常规的InvokeRequired逻辑。

#2


3  

For your first question, I am not entirely sure, but a review from online seems to show that sometimes this will not throw an exception, but it will not update the label. Is that the case here? Is your label being updated along with having no exception?

对于你的第一个问题,我不是很确定,但网上的一篇评论似乎表明,有时这不会抛出异常,但不会更新标签。是这样吗?你的标签是否被更新,并且没有例外?

However, I can answer you second question right now. The ProgressChanged event is meant for exactly what it sounds like. It is supposed to be called to let the UI thread know the status of the backgroundworker so that it can update itself appropriately. The original calling thread (UI in this case) is the one that is used for the ProgressChanged, so when it updates it does not need to call Invoke. But, this should really only be done for showing the progress of a background worker.

但是,我现在可以回答你的第二个问题。ProgressChanged事件的含义与它听起来的完全一样。应该调用它来让UI线程知道backgroundworker的状态,以便它能够适当地更新自己。初始调用线程(在本例中为UI)是用于ProgressChanged的线程,因此当它更新时,它不需要调用Invoke。但是,这仅仅是为了显示后台工作人员的进展。

Now, if it is not an update that you are trying to pass to the calling method, then I would suggest just passing your return data back through the RunWorkerCompleted event. This passes all of your final data back up to the original (UI) thread, so that it can update the UI without any need for an Invoke.

现在,如果您试图传递给调用方法的不是更新,那么我建议您只需通过RunWorkerCompleted事件传递返回数据。这将把所有最终数据备份到原始(UI)线程,这样它就可以在不需要调用的情况下更新UI。

So, yes your call to Invoke will work, though. However, understanding what each of the other events are for can help you understand why to use one way over another. Maybe a ProgressChanged event fits better? It can also declutter your code from having unnecessary invokes.

所以,是的,您调用的调用将会工作。但是,理解其他事件的用途可以帮助您理解为什么要使用一种方式而不是另一种方式。也许进步改变的事件更适合?它还可以减少不必要的调用。

Update to first q

更新第一个问

I still cannot find anything about the toolstrip not needing the invoke. In fact I am finding the opposite using google searches like "toolstriplabel no cross thread exception" or "toolstriplabel invoke", etc. However, as henk mentioned, the toolstriplabel doesn't inherit from control so that might explain why no invoke is required. However, my suggestion is to assume that it will act like any other UI control and make sure it is updated on the UI thread to be safe. do not rely on quirks. Better safe than sorry, you never know if things like this might change, especially since it is logically a UI item to most..,

我仍然无法找到不需要调用的工具条。实际上,我使用谷歌搜索找到了相反的结果,比如“工具striplabel no cross thread exception”或“toolstriplabel invoke”,等等。但是,我的建议是假设它将像任何其他UI控件一样运行,并确保在UI线程上更新它以确保安全。不要依赖怪癖。比抱歉更安全的是,你永远不知道这样的事情是否会改变,特别是因为它在逻辑上是大多数人的UI项。

#3


0  

The advantage of your second choice is that it works :)

你的第二选择的好处是它是有效的:

All UI elements are created on main UI thread and, what is more important from this question perspective, is that can be acessed only within that thread.

所有UI元素都是在主UI线程上创建的,从这个问题的角度来看,更重要的是,它只能在该线程中被嵌入。

This is the reason why your first case fails and that is the reason your second case will work. Invoke()... will redirect required merhod call to the main UI thread.

这就是第一个案例失败的原因,也是第二个案例成功的原因。Invoke()……重定向需要merhod调用主UI线程。

Hope this helps.

希望这个有帮助。

#1


5  

To the first question:

第一个问题:

  • the cross-thread exception is deliberately thrown in Debug mode. This means there is (conditional) code checking on InvokeRequired built into most of the GUI controls. Like the Panel.

    在调试模式下故意抛出交叉线程异常。这意味着在大多数GUI控件中内置的InvokeRequired(条件)代码检查。像面板。

  • Apparently the ToolstripLabel does not make this check. Since it does not derive from Control that could be because it is outside the scope of this safety net.

    显然工具条标签没有做这个检查。因为它不是来自控制,这可能是因为它超出了这个安全网的范围。

Since the standard disclaimer "Any instance members are not guaranteed to be thread safe" applies to the ToolstripLabel I would just go with the normal InvokeRequired logic when setting the Text.

由于标准的免责声明“任何实例成员都不能保证是线程安全的”适用于工具标记,所以在设置文本时,我将使用常规的InvokeRequired逻辑。

#2


3  

For your first question, I am not entirely sure, but a review from online seems to show that sometimes this will not throw an exception, but it will not update the label. Is that the case here? Is your label being updated along with having no exception?

对于你的第一个问题,我不是很确定,但网上的一篇评论似乎表明,有时这不会抛出异常,但不会更新标签。是这样吗?你的标签是否被更新,并且没有例外?

However, I can answer you second question right now. The ProgressChanged event is meant for exactly what it sounds like. It is supposed to be called to let the UI thread know the status of the backgroundworker so that it can update itself appropriately. The original calling thread (UI in this case) is the one that is used for the ProgressChanged, so when it updates it does not need to call Invoke. But, this should really only be done for showing the progress of a background worker.

但是,我现在可以回答你的第二个问题。ProgressChanged事件的含义与它听起来的完全一样。应该调用它来让UI线程知道backgroundworker的状态,以便它能够适当地更新自己。初始调用线程(在本例中为UI)是用于ProgressChanged的线程,因此当它更新时,它不需要调用Invoke。但是,这仅仅是为了显示后台工作人员的进展。

Now, if it is not an update that you are trying to pass to the calling method, then I would suggest just passing your return data back through the RunWorkerCompleted event. This passes all of your final data back up to the original (UI) thread, so that it can update the UI without any need for an Invoke.

现在,如果您试图传递给调用方法的不是更新,那么我建议您只需通过RunWorkerCompleted事件传递返回数据。这将把所有最终数据备份到原始(UI)线程,这样它就可以在不需要调用的情况下更新UI。

So, yes your call to Invoke will work, though. However, understanding what each of the other events are for can help you understand why to use one way over another. Maybe a ProgressChanged event fits better? It can also declutter your code from having unnecessary invokes.

所以,是的,您调用的调用将会工作。但是,理解其他事件的用途可以帮助您理解为什么要使用一种方式而不是另一种方式。也许进步改变的事件更适合?它还可以减少不必要的调用。

Update to first q

更新第一个问

I still cannot find anything about the toolstrip not needing the invoke. In fact I am finding the opposite using google searches like "toolstriplabel no cross thread exception" or "toolstriplabel invoke", etc. However, as henk mentioned, the toolstriplabel doesn't inherit from control so that might explain why no invoke is required. However, my suggestion is to assume that it will act like any other UI control and make sure it is updated on the UI thread to be safe. do not rely on quirks. Better safe than sorry, you never know if things like this might change, especially since it is logically a UI item to most..,

我仍然无法找到不需要调用的工具条。实际上,我使用谷歌搜索找到了相反的结果,比如“工具striplabel no cross thread exception”或“toolstriplabel invoke”,等等。但是,我的建议是假设它将像任何其他UI控件一样运行,并确保在UI线程上更新它以确保安全。不要依赖怪癖。比抱歉更安全的是,你永远不知道这样的事情是否会改变,特别是因为它在逻辑上是大多数人的UI项。

#3


0  

The advantage of your second choice is that it works :)

你的第二选择的好处是它是有效的:

All UI elements are created on main UI thread and, what is more important from this question perspective, is that can be acessed only within that thread.

所有UI元素都是在主UI线程上创建的,从这个问题的角度来看,更重要的是,它只能在该线程中被嵌入。

This is the reason why your first case fails and that is the reason your second case will work. Invoke()... will redirect required merhod call to the main UI thread.

这就是第一个案例失败的原因,也是第二个案例成功的原因。Invoke()……重定向需要merhod调用主UI线程。

Hope this helps.

希望这个有帮助。