【问题标题】:Pass arguments by reference to BackgroundWorker通过引用 BackgroundWorker 传递参数
【发布时间】:2013-04-05 16:12:35
【问题描述】:

我需要调用一个 C dll 作为后台进程。 dll 的执行很耗时,因此我想在主 GUI 上显示一个进度条。为了使用 ReportProgress,我需要能够通过引用传递两个参数,并在参数值发生变化时更新进度条。但是,RunWorkerAsync 似乎只接受值(而不是引用)。

我该怎么做?

谢谢。

这里是简化的代码:

public void button1_Click(object sender, EventArgs e)
    {
       //Variable declarations and initializations

       List<object> arguments = new List<object>();
                arguments.Add(curgen);
                arguments.Add(dataindex);

        backgroundWorker1.RunWorkerAsync(arguments);
        backgroundWorker1.ReportProgress(curgen * 100 / ngen, "GEN");
        backgroundWorker1.ReportProgress(dataindex * 100 / (DIMENSION * FITNESSCASES), "DATA");

    }

 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        List<object> genericlist = e.Argument as List<object>;
        // Getting variables from object
         calldll.gpinnovization(ref curgen, ref dataindex);
    }

我想通过引用传递“curgen”和“dataindex”。

【问题讨论】:

  • 你能发布你的代码吗?
  • 这当然行不通,调用RunWorkerAsync()的代码早就没有了。简单的解决方法是更新 RunWorkerCompleted 事件处理程序中的变量。
  • @HansPassant 据我了解, RunWorkerAsync() 是一个不同的线程。如果在 gpinnovization() 中更新了 'curgen' 和 'dataindex',那么 ReportProgress 应该能够更新进度条。请详细说明。

标签: c# winforms visual-studio-2010


【解决方案1】:

您可以为 DoWork 事件处理程序使用 lambda 来关闭变量(请注意,闭包关闭变量,而不是值,因此这不会只是复制值)。从那里您可以在 lambda 中直接完成工作,或者通过引用将变量传递给其他方法。

int someValue = 5;

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (s, args) =>
{
    ProcessValue(ref someValue);
};
worker.RunWorkerAsync();

【讨论】:

  • 但是你没有向 RunWorkerAsync 传递任何东西。我错过了什么吗?
  • @Sunith 正确,我不是。我认为没有必要将任何东西传递给RunWorkerAsync。曾经。在添加 DoWork 事件处理程序时,只需关闭这些值即可。它既简单又有效。
  • 如果我想在ReportProgress中使用someValue,我该怎么做?我应该把ProgrssChanged放在哪里?
  • @Sunith 您可以在DoWork 事件处理程序之前或之后添加进度更改事件处理程序。当您调用ReportProgress 时,您可以通过它的参数传递一个变量,该变量可以在ProgressChanged 的参数中访问。
【解决方案2】:

为了他人的利益,我最终是这样做的。感谢 Servy 建议 lambda 表达式:

        BackgroundWorker backgroundWorker1 = new BackgroundWorker();
        backgroundWorker1.WorkerReportsProgress = true;
        backgroundWorker1.DoWork += (s, args) =>
            {
                Mydll.MyCfunction(ref curgen, ref dataindex);
            };
        backgroundWorker1.RunWorkerAsync();
        backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;


        while (backgroundWorker1.IsBusy)
        {
            backgroundWorker1.ReportProgress(curgen * 100 / ngen, "GEN");
            backgroundWorker1.ReportProgress(dataindex * 100 / (DIMENSION * FITNESSCASES), "DATA");
            Application.DoEvents();
        }

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e1)
    {
        switch (e1.UserState.ToString())
        {
            case "GEN":
                progressBar1.Value = e1.ProgressPercentage;
                break;
            case "DATA":
                progressBar2.Value = e1.ProgressPercentage;
                break;
        }

    }

【讨论】:

  • 1) 您多次附加ProgressChanged 事件处理程序。你应该只做一次。 2)不要使用DoEvents,尤其不要这样。你没有正确使用它。
  • @Servy 我改变了答案。这是正确的方法吗?虽然它有效,但我不知道它是否可靠。
  • 不,它仍然存在我已经概述的所有问题。
  • 问题是curgendataindexMydll.MyCfunction 内部更新。如何在不使用 while(isbusy) 循环的情况下报告进度。
  • 如果您无法在执行工作的函数中报告进度,则启动一个计时器并在计时器的滴答事件中报告进度。您可以在为计时器附加刻度事件处理程序时关闭相同的变量,以确保它们都访问相同的变量。
猜你喜欢
  • 2010-10-07
  • 2011-08-21
  • 2017-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-15
  • 2017-03-21
相关资源
最近更新 更多