【问题标题】:Cross Threading Exception - Using Invoke跨线程异常 - 使用调用
【发布时间】:2012-02-24 10:14:37
【问题描述】:

收到跨线程异常错误后,我在MSDN上查了一下。

我尝试在那里实现一些代码,但无法让回调工作。

addItemCallback d = new addItemCallback(addItem);

这位于下面的 addItem() 方法中。

我正在尝试将项目列表添加到列表框中,最终每次找到一个值时都会更新表单,而不是在后台程序完成工作后添加所有项目。

private void startWork()
{        
    progressBar1.Value = 0;

    progressBar1.Maximum = 901242;

    backgroundWorker1.RunWorkerAsync();    
}

private void getList()
{
    if (pathFound)
    {
        for (int i = 0; i < numberOfPaths; i++)
        {
            Microsoft.Win32.RegistryKey mainPath = secondaryPath.OpenSubKey("application " + Convert.ToString(i));

            if (mainPath != null)
            {
                    this.addItem((string)mainPath.GetValue("Name"));
            }

            backgroundWorker1.ReportProgress(i);
        }
    }

    pathListBox.Sorted = true;
}

private void addItem(string item)
{
    if (this.pathListBox.InvokeRequired)
    {

        //addItemCallback d = new addItemCallback(addItem); 

        //not sure what this callBack is, can't get it to work, Callback isnt found.

        this.Invoke(d, new object[] { item });
    }

    else 
    {
        this.pathListBox.Items.Add(item);
    }
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    getList();
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar1.Value = e.ProgressPercentage;
}

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    this.progressBar1.Visible = false;
}

工作至今

代码 1. 当我使用 background_doWork 并从中调用一个方法时,进度条挂在随机位置并停止响应,在关闭表单时我得到一个对象异常,因为我在它仍在尝试时关闭了表单干活。

代码 2。当我将所有代码放在 background_doWork 中而不是从中调用方法时,进度条有时会工作,每秒或每 3 次尝试运行它完成的程序。

这是什么原因造成的?

-----代码 1------------

    public Form1()
    {
        InitializeComponent();

        start();
    }

    int number = 900000;

    public void start()
    {
        progressBar1.Value = 0;

        progressBar1.Maximum = number;

        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        getList();
    }

    private void getList()
    {
        Microsoft.Win32.RegistryKey mainPath = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node");

        for (int i = 0; i < number; i++)
        {
            Microsoft.Win32.RegistryKey mainPath = secondaryPath.OpenSubKey("application " + Convert.ToString(i));

            if (mainPath != null)
            {
                this.addItem((string)mainPath.GetValue("Name"));
            }

            backgroundWorker1.ReportProgress(i);
        }
    }

    private void addItem(string item)
    {
        try
        {

            if (this.listBox1.InvokeRequired)
            {
                this.Invoke(new Action<string>(addItem), item);
            }

            else
            {
                this.listBox1.Items.Add(item);
            }
        }

        catch
        {
            MessageBox.Show("Error - Closed Object before it finished working.");
        }

        //this.steamGamesListBox.Sorted = true;
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        this.progressBar1.Visible = false;
    }

--------代码 2 --------

    public Form1()
    {
        InitializeComponent();

        start();
    }

    int number = 900000;

    public void start()
    {
        progressBar1.Value = 0;

        progressBar1.Maximum = number;

        backgroundWorker1.RunWorkerAsync();
    }

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        Microsoft.Win32.RegistryKey steamApps64 = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall");

        for (int i = 0; i < number; i++)
        {
            Microsoft.Win32.RegistryKey steamApps = steamApps64.OpenSubKey("Steam App " + Convert.ToString(i));

            if (steamApps != null)
            {
                this.addItem((string)steamApps.GetValue("DisplayName"));
            }

            backgroundWorker1.ReportProgress(i);
        }
    }

    private void addItem(string item)
    {
        try
        {

            if (this.listBox1.InvokeRequired)
            {
                this.Invoke(new Action<string>(addItem), item);
            }

            else
            {
                this.listBox1.Items.Add(item);
            }
        }

        catch
        {
            MessageBox.Show("Error - Closed Object before it finished working.");
        }

        //this.steamGamesListBox.Sorted = true;
    }

    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar1.Value = e.ProgressPercentage;
    }

    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        this.progressBar1.Visible = false;
    }

【问题讨论】:

  • 您使用的是哪个版本的 C#/Framework - 根据这一点,您需要为 Invoke 创建自己的委托,或者您可以使用 Action 之类的东西
  • 使用 .net4 :) (字符限制:P)

标签: c# progress-bar backgroundworker multithreading registrykey


【解决方案1】:

您要么需要定义自定义委托类型(您的addItemCallback),要么只需使用通用的Action 委托:

private void addItem(string item)
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new Action<string>(addItem), item);
        return;
    }

    this.pathListBox.Items.Add(item);
}

这通常是最简单的方法,因为您不需要引入额外的委托类型。

请注意,object[] 参数是使用 params 关键字定义的,因此您无需实例化新的 object 数组,只需将参数传递给方法即可。

【讨论】:

  • 啊,这很好用!我只有一个问题,那就是当我运行它时,列表会更新,但进度条卡在中间。看起来程序无法通过 this.Invoke(new Action(addItem), item); - 一旦我关闭程序,我得到一个对象处理异常(因为我关闭了它),任何想法为什么它会卡在这里?
  • 通常,最好使用BeginInvoke 而不是Invoke 以避免死锁(查看我更新的帖子)。而且我还会从您的后台方法中删除pathListBox.Sorted = true;(您应该与该线程中的控件没有交互)。不过,我不确定为什么这会发生在“中间”。我想你应该打电话给backgroundWorker1.ReportProgress(100 * i / numberOfPaths);,但现在你可能有大约 50 条路径,所以你的处理实际上到此结束(这是我的猜测)。尝试进行这些更改,看看效果如何。
  • 请注意,在这种情况下您可以使用BeginInvoke,因为添加项目的顺序并不重要,如果Sorted 属性设置为true,它们将被插入到位。另一方面,如果你对此有问题,你可以尝试完全改变结构:使用worker填充一个List(你可以在后台线程中完成),然后将列表传递给ListBox,当你完了。根据正在完成的工作的类型和项目的数量,这可能也很有效。
  • 不要这样做,它会非常慢并且仍然使 UI 线程变得紧张。让您的工作人员填写项目列表,在 RunWorkerCompleted 事件处理程序中调用 pathListBox.Items.AddRange()。
  • 使用invoke,列表一次更新1个,进度条挂在随机位置。使用 BeginInvoke 修复了挂起,但随着列表的更新,它保持全白,随着垂直滚动增加,一旦进度条到达末尾,所有值都会显示:P
【解决方案2】:

添加delegate void addItemCallback(string item);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多