代码之家  ›  专栏  ›  技术社区  ›  Kevin

为什么会出现此错误:“跨线程操作无效:从创建它的线程以外的线程访问控件lbfolders。”?

  •  2
  • Kevin  · 技术社区  · 16 年前

    这让我困惑,也许有人能把教育的光芒照在我的无知上。这是一个C Windows应用程序。我正在从线程访问列表框的内容。当我试图这样访问它时

    prgAll.Maximum = lbFolders.SelectedItems.Count;
    我得到错误。不过,这是我无法理解的部分。如果我对那一行作评论,下一行
    foreach (string dir in lbFolders.SelectedItems)
    执行得很好。

    编辑: 像往常一样,我的沟通能力很差。让我澄清一下。

    我知道,从线程访问GUI项(而不是在线程上创建的)会导致问题。我知道访问它们的正确方法是通过代理。

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

    7 回复  |  直到 10 年前
        1
  •  6
  •   arul    16 年前
    prgAll.Maximum = lbFolders.SelectedItems.Count;
    

    在那条线上你执行一项任务( 设置/添加 ,这在默认情况下不是线程安全的。

    在第二行,这只是一个 得到 操作,其中线程安全并不重要。

    编辑:我不是说访问prgal元素。

    访问Count属性 更改内部状态 对于ListBox内部集合,这就是它引发异常的原因。

        2
  •  17
  •   Echostorm    16 年前

    不能从单独的线程访问GUI元素。使用代理进行更改。

    如。

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

    或更老的Skoo:

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

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

        3
  •  3
  •   Martin Marconcini    16 年前

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

        4
  •  2
  •   Jon B    16 年前

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

    void SetMax()
    {
        if (prgAll.InvokeRequired)
        {
            prgAll.BeginInvoke(new MethodInvoker(SetMax));
            return;
        }
    
        prgAll.Maximum = lbFolders.SelectedItems.Count;
    }
    
        5
  •  1
  •   Community CDub    7 年前

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

        6
  •  1
  •   sebagomez    16 年前

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

    private void RunMe()
    {
        if (!InvokeRequired)
        {
            myLabel.Text = "You pushed the button!";
        }
        else
        {
            Invoke(new ThreadStart(RunMe));
        }
    }
    
        7
  •  0
  •   Gatherer    10 年前

    试试这个:

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

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