为什么我会收到此错误:“跨线程操作无效:控制从其创建的线程以外的线程访问的lbFolders。”?

时间:2022-01-11 10:58:22

This is baffling me, maybe somebody can shine the light of education on my ignorance. This is in a C# windows app. I am accessing the contents of a listbox from a thread. When I try to access it like this

这让我感到困惑,也许有人可以用我的无知来照亮教育之光。这是在C#windows应用程序中。我从一个线程访问列表框的内容。当我尝试像这样访问它时

prgAll.Maximum = lbFolders.SelectedItems.Count;
I get the error. However, here is the part I don't get. If I comment out that line, the very next line
foreach (string dir in lbFolders.SelectedItems)
executes just fine.

Edit: As usual, my communication skills are lacking. Let me clarify.

编辑:像往常一样,我缺乏沟通技巧。让我澄清一下。

I know that accessing GUI items from threads other than the ones they were created on causes problems. I know the right way to access them is via delegate.

我知道从除了创建它们之外的线程访问GUI项会导致问题。我知道访问它们的正确方法是通过委托。

My question was mainly this: Why can I access and iterate through the SelectedItems object just fine, but when I try to get (not set) the Count property of it, it blows up.

我的问题主要在于:为什么我可以正常访问和迭代SelectedItems对象,但是当我尝试获取(未设置)它的Count属性时,它会爆炸。

7 个解决方案

#1


6  

prgAll.Maximum = lbFolders.SelectedItems.Count;

On that line you perform an assignment (set/add), which by default is not thread-safe.

在该行上,您执行一个赋值(set / add),默认情况下它不是线程安全的。

On the second line it's just a get operation, where thread-safety merely doesn't matter.

在第二行,它只是一个get操作,其中线程安全无关紧要。

EDIT: I don't mean access to the prgAll element.

编辑:我不是指访问prgAll元素。

Accessing the Count property changes the internal state of the ListBox inner collection, that is why it throws the exception.

访问Count属性会更改ListBox内部集合的内部状态,这就是它抛出异常的原因。

#2


17  

You can't access GUI elements from a separate thread. Use a delegate to make the change.

您无法从单独的线程访问GUI元素。使用委托进行更改。

eg.

lblStatus.Invoke((Action)(() => lblStatus.Text = counter.ToString()));

or older skool:

或更旧的skool:

lblTest.Invoke((MethodInvoker)(delegate() 
{ 
  lblTest.Text = i.ToString(); 
}));

I've got a blog post on how to do this in all the .Net releases here.

我有一篇关于如何在所有.Net版本中执行此操作的博客文章。

#3


3  

The Count property of SelectedItems is not thread-safe, so you can't use it cross-thread.

SelectedItems的Count属性不是线程安全的,因此您不能跨线程使用它。

#4


2  

You're trying to write to a control from a thread other than the main thread. Use Invoke or BeginInvoke.

您正在尝试从主线程以外的线程写入控件。使用Invoke或BeginInvoke。

void SetMax()
{
    if (prgAll.InvokeRequired)
    {
        prgAll.BeginInvoke(new MethodInvoker(SetMax));
        return;
    }

    prgAll.Maximum = lbFolders.SelectedItems.Count;
}

#5


1  

You can't touch a GUI object from a thread that isn't the main GUI thread. See here for more details and the solution.

您无法从不是主GUI线程的线程触摸GUI对象。有关详细信息和解决方案,请参见此处。

#6


1  

Because you created a control in a thread and you're trying to reach it from another one. Call the InvokeRequired property as shown here:

因为您在线程中创建了一个控件,并且您正试图从另一个控件中获取该控件。调用InvokeRequired属性,如下所示:

private void RunMe()
{
    if (!InvokeRequired)
    {
        myLabel.Text = "You pushed the button!";
    }
    else
    {
        Invoke(new ThreadStart(RunMe));
    }
}

#7


0  

Try this:

private delegate void xThreadCallBack();
private void ThreadCallBack()
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new xThreadCallBack(ThreadCallBack));
    }
    else
    {
        //do what you want
    }
}

Though, the answer with the lambda expression would suffice.

但是,使用lambda表达式的答案就足够了。

#1


6  

prgAll.Maximum = lbFolders.SelectedItems.Count;

On that line you perform an assignment (set/add), which by default is not thread-safe.

在该行上,您执行一个赋值(set / add),默认情况下它不是线程安全的。

On the second line it's just a get operation, where thread-safety merely doesn't matter.

在第二行,它只是一个get操作,其中线程安全无关紧要。

EDIT: I don't mean access to the prgAll element.

编辑:我不是指访问prgAll元素。

Accessing the Count property changes the internal state of the ListBox inner collection, that is why it throws the exception.

访问Count属性会更改ListBox内部集合的内部状态,这就是它抛出异常的原因。

#2


17  

You can't access GUI elements from a separate thread. Use a delegate to make the change.

您无法从单独的线程访问GUI元素。使用委托进行更改。

eg.

lblStatus.Invoke((Action)(() => lblStatus.Text = counter.ToString()));

or older skool:

或更旧的skool:

lblTest.Invoke((MethodInvoker)(delegate() 
{ 
  lblTest.Text = i.ToString(); 
}));

I've got a blog post on how to do this in all the .Net releases here.

我有一篇关于如何在所有.Net版本中执行此操作的博客文章。

#3


3  

The Count property of SelectedItems is not thread-safe, so you can't use it cross-thread.

SelectedItems的Count属性不是线程安全的,因此您不能跨线程使用它。

#4


2  

You're trying to write to a control from a thread other than the main thread. Use Invoke or BeginInvoke.

您正在尝试从主线程以外的线程写入控件。使用Invoke或BeginInvoke。

void SetMax()
{
    if (prgAll.InvokeRequired)
    {
        prgAll.BeginInvoke(new MethodInvoker(SetMax));
        return;
    }

    prgAll.Maximum = lbFolders.SelectedItems.Count;
}

#5


1  

You can't touch a GUI object from a thread that isn't the main GUI thread. See here for more details and the solution.

您无法从不是主GUI线程的线程触摸GUI对象。有关详细信息和解决方案,请参见此处。

#6


1  

Because you created a control in a thread and you're trying to reach it from another one. Call the InvokeRequired property as shown here:

因为您在线程中创建了一个控件,并且您正试图从另一个控件中获取该控件。调用InvokeRequired属性,如下所示:

private void RunMe()
{
    if (!InvokeRequired)
    {
        myLabel.Text = "You pushed the button!";
    }
    else
    {
        Invoke(new ThreadStart(RunMe));
    }
}

#7


0  

Try this:

private delegate void xThreadCallBack();
private void ThreadCallBack()
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new xThreadCallBack(ThreadCallBack));
    }
    else
    {
        //do what you want
    }
}

Though, the answer with the lambda expression would suffice.

但是,使用lambda表达式的答案就足够了。