【问题标题】:Why am I getting this error:"Cross-thread operation not valid: Control lbFolders accessed from a thread other than the thread it was created on."?为什么我会收到此错误:“跨线程操作无效:控制 lbFolders 从创建它的线程以外的线程访问。”?
【发布时间】:2008-10-28 19:49:03
【问题描述】:

这让我感到莫名其妙,也许有人可以将教育之光照亮我的无知。这是在 C# Windows 应用程序中。我正在从线程访问列表框的内容。当我尝试像这样访问它时

prgAll.Maximum = lbFolders.SelectedItems.Count;
我得到了错误。但是,这是我没有得到的部分。如果我注释掉那一行,下一行
foreach (string dir in lbFolders.SelectedItems)
执行得很好。

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

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

我的问题主要是这样的: 为什么我可以很好地访问和遍历 SelectedItems 对象,但是当我尝试获取(未设置)它的 Count 属性时,它就崩溃了。

【问题讨论】:

  • 这里的大多数答案都是正确的,但您说它们是错误的,因为它们不适合您,并且可能是您投票否决了它们。这让我对你投了反对票。
  • 如果您将鼠标悬停在赞成或反对按钮上,您会看到它说“这很有帮助”或“这没有帮助”只是告诉我我不能做我能做的事情做没有帮助。更不用说它实际上并没有解决我提出的问题。
  • 至于你怎么给我的问题投票,我不在乎。
  • 问题是他们都解释了一个简单的规则:不要做跨线程GUI的东西,因为那是错误的原因。您问的唯一问题是“为什么我会收到此错误”,他们都解释了原因。您没有确切地问为什么在某些情况下您没有收到该错误。
  • @"我可以不在乎":很好,但我想知道为什么我被否决了。正因为如此,我总是写下我为什么要投票给别人。称之为礼貌。

标签: c# .net multithreading


【解决方案1】:

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

例如。

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

或更老的学校:

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

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

【讨论】:

    【解决方案2】:
    prgAll.Maximum = lbFolders.SelectedItems.Count;
    

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

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

    编辑:我的意思不是访问 prgAll 元素。

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

    【讨论】:

    • 不,错误的答案。我把它分解了,在获取 Count 值时发生了错误,而不是在设置最大值时发生了错误
    • +1 表示关于内部状态的评论。不幸的是,凯文不喜欢你的回答,即使它是真的。
    • 实际上,他编辑的答案更有意义。将我的 -1 更改为 +1
    • 当然,为什么 M$ 设计成那样仍然没有意义,但这不是 Lykathea 的问题
    • 我认为原因是他们只在真正重要的时候测试线程间调用。一种优化。
    【解决方案3】:

    SelectedItems 的 Count 属性不是线程安全的,所以不能跨线程使用。

    【讨论】:

    • 谢谢,这是唯一甚至开始回答问题的答案。那么,SelectedItems 对象是线程安全的,但它的属性不是吗?这对我来说没有意义。不是说你错了,只是没有意义。
    • 不管它是否有意义,很明显跨线程是问题所在,您应该调用回 gui 线程来访问这些属性。要查看某个东西是否是线程安全的,请在 MSDN 库中查找它——它清楚地说明了线程安全。
    • 我确实在 MSDN 上同时查找了 SelectedItems 对象和 Count 属性,我没有看到任何关于“线程安全”的任何内容。
    【解决方案4】:

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

    void SetMax()
    {
        if (prgAll.InvokeRequired)
        {
            prgAll.BeginInvoke(new MethodInvoker(SetMax));
            return;
        }
    
        prgAll.Maximum = lbFolders.SelectedItems.Count;
    }
    

    【讨论】:

    • 不,错误的答案。我把它分解了,在获取 Count 值时发生了错误,而不是在设置最大值时发生了错误
    • @Kevin 您是否有机会从不同的线程创建 prgAll 和 lblFolders?
    • @Kevin 使用 prgAll.Invoke 而不是 this.Invoke 可能会为您工作。
    【解决方案5】:

    您不能从不是主 GUI 线程的线程中触摸 GUI 对象。有关详细信息和解决方案,请参阅here

    【讨论】:

    • 哎呀,对不起,我不是故意要删除你的评论
    • 没问题,我会重新添加它。您的回答没有意义,因为设置 prgAll.Maximum 的值并没有爆炸,它也不会在访问 SelectItems 本身时爆炸,只是在 selecteditems.count 上
    • @Kevin:马特的回答很有道理。虽然您通常无法从另一个线程访问 GUI 元素,但并非所有属性或方法都会受此影响。规则很简单:不要在 GUI 线程以外的线程中接触 GUI 对象。请参阅 Echostorms 答案以了解如何正确操作。
    • @OregonGhost:不,你的回答有道理,至少更有意义。他说不通,因为他告诉我我不能做我显然可以做的事情。
    • @OregonGhost。但是,我可以访问 ListBox.selecteditems 并毫无问题地遍历它对我来说仍然没有意义,但是如果我尝试访问 ListBox.SelectedItems.Count,它就会崩溃。
    【解决方案6】:

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

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

    【讨论】:

    • 这给了我“'函数名'没有重载匹配委托'System.Threading.ThreadStart'
    • 这篇文章有帮助:codeproject.com/Articles/20081/… 但它没有解释如何使用参数调用函数。我的“RunMe”函数有两个参数,没有它们的情况下编写委托会抛出“System.Reflection.TargetParameterCountException”将它们作为委托的一部分添加会导致编译器错误:“预期方法名称”
    • 想通了!您添加第二个参数来调用以传递您的参数。像这样:this.BeginInvoke(new DoThreadedGoodManualType(DoThreadedGoodManual), new Object[] {param1, param2};));
    【解决方案7】:

    试试这个:

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

    不过,使用 lambda 表达式的答案就足够了。

    【讨论】:

      猜你喜欢
      • 2013-07-06
      • 2011-01-15
      • 2016-05-21
      • 2016-06-11
      • 2015-05-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多