跨线程分配属性

时间:2010-04-13 12:53:20

标签: c# multithreading

我之前在线程中设置了一个属性,我发现这篇文章Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on有关获取属性的信息。

我认为我对下面代码的问题是将变量设置为集合是一个对象因此在堆上因此只是创建指向同一对象的指针

所以我的问题是除了创建深层副本,或者将集合复制到不同的List对象之外,还有更好的方法来执行以下操作以在for循环期间避免错误。

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

代码:

    private void btnRunProcess_Click(object sender, EventArgs e)
    {
        richTextBox1.Clear();

        BackgroundWorker bg = new BackgroundWorker();
        bg.DoWork += new DoWorkEventHandler(bg_DoWork);
        bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
        bg.RunWorkerAsync();
   }


    void bg_DoWork(object sender, DoWorkEventArgs e)
    {
        WorkflowEngine engine = new WorkflowEngine();
        ListBox.SelectedObjectCollection selectedCollection=null;

        if (lstProcessFiles.InvokeRequired)
        {
            // Try #1
            selectedCollection = (ListBox.SelectedObjectCollection) 
                this.Invoke(new GetSelectedItemsDelegate(GetSelectedItems), 
                new object[] { lstProcessFiles });

            // Try #2
            //lstProcessFiles.Invoke(
            //    new MethodInvoker(delegate {
            //        selectedCollection = lstProcessFiles.SelectedItems; }));
        }
        else
        {
            selectedCollection = lstProcessFiles.SelectedItems;
        }         

        // *********Same Error on this line********************
        // Cross-thread operation not valid: Control 'lstProcessFiles' accessed 
        // from a thread other than the thread it was created on.
        foreach (string l in selectedCollection)
        {
            if (engine.LoadProcessDocument(String.Format(@"C:\TestDirectory\{0}", l)))
            {
                try
                {
                    engine.Run();                       
                    WriteStep(String.Format("Ran {0} Succussfully", l));
                }
                catch
                {
                    WriteStep(String.Format("{0} Failed", l));
                }

                engine.PrintProcess();
                WriteStep(String.Format("Rrinted {0} to debug", l));
            }
        }
    }

    private delegate void WriteDelegate(string p);
    private delegate ListBox.SelectedObjectCollection GetSelectedItemsDelegate(ListBox list);

    private ListBox.SelectedObjectCollection GetSelectedItems(ListBox list)
    {
        return list.SelectedItems;
    }

4 个答案:

答案 0 :(得分:1)

看看this SO question - 它解决了类似的话题。

在许多UI技术(Winforms,WPF,Silverlight)中 - UI元素只能在UI线程上安全地进行交互。这意味着在编写多线程代码时,需要使用您的UI库选择与UI控件正确交互。在WPF / Silverlight中,Dispatcherin WinForms需要使用InvokeRequired方法和BeginInvoke()将工作分派给UI线程。

在您的情况下,您似乎已经尝试使用BeginInvoke分派到相应的线程。 我怀疑问题是迭代ListBox的集合本身就是一个跨线程的操作。你不知道SelectedItems集合如何实现GetEnumerator() - 尽管它最有可能不复制该集合。所以我怀疑你的代码必须在迭代它之前制作一个副本 - 或者在UI线程上执行整个迭代。

制作该集合的副本并不坏,但同样,副本必须在UI线程上进行 - 它可能需要是一个深层副本,因为它包含其他UI对象(ListBoxItem)。如果您使用的是.NET 3.5,则可以使用LINQ在代码中投影匿名对象,而不是尝试制作UI元素的深层副本。

答案 1 :(得分:1)

您必须使用Control的InvokeRequired属性来避免从其他线程调用一个控件。

检查例如this page

答案 2 :(得分:0)

您正在传递此行中的所选项目:

bg.RunWorkerAsync(lstProcessFiles.SelectedItems);

为什么要尝试在DoWork方法中再次使用它们?

使用以下命令从DoworkEventArgs访问它们:

var collection = (ListBox.SelectedObjectCollection)e.Argument

(在调用后台工作程序之前,您可能仍需要将所选对象复制到普通列表中,我不确定哪些对象存在于该专用集合类型中)

答案 3 :(得分:0)

我刚刚制作了一个字符串对象的副本。即使它是一个更复杂的对象,这样的东西仍然可以工作

private List<string> GetSelectedItems(ListBox list)
{
    return lstProcessFiles.SelectedItems.Cast<string>().ToList();
}