【问题标题】:DoWork of BackgroundWorker is called twice when RunWorkerAsync is called once?一次调用 RunWorkerAsync 时,BackgroundWorker 的 DoWork 被调用两次?
【发布时间】:2012-09-01 21:01:33
【问题描述】:

我在一个可以工作的类中创建了一个后台工作程序,但是如果我调用并等到最后运行,第二次调用它,它将执行两次相同的过程

我认为 bw.DoWork +=

有问题
private void button1_Click(object sender, EventArgs e)
{
    nptest.test.start("null", "null");    
}


namespace nptest
{
    class test
    {
        public static void start(string str, string strb)
        {
            if (bw.IsBusy != true)
            {
                bw.WorkerSupportsCancellation = true;
                bw.DoWork += (obj, e) => bw_DoWork(str, strb);
                bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
                bw.RunWorkerAsync();
            }
        }
        private static BackgroundWorker bw = new BackgroundWorker();
        private static void bw_DoWork(string str, string strb)
        {
            System.Windows.Forms.MessageBox.Show("initializing BackgroundWorker");
        }
        private static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if ((e.Cancelled == true))
            {
                Console.WriteLine("Canceled");
            }
            else if (!(e.Error == null))
            {
                Console.WriteLine("Error: " + e.Error.Message);
            }
            bw.Dispose();

        }
    }
}

问题已解决

  class test
    {
        private static List<object> arguments = new List<object>();

        // initializing with program startup
        public static void bwinitializing()
        {
            bw.WorkerSupportsCancellation = true;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
        }

        public static void start(string str, string strb)
        {
            if (bw.IsBusy != true)
            {
                arguments.Clear();
                arguments.Add(str);
                arguments.Add(strb);
                bw.RunWorkerAsync(arguments);
            }
        }
        private static BackgroundWorker bw = new BackgroundWorker();
        private static void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            List<object> genericlist = e.Argument as List<object>;
            System.Windows.Forms.MessageBox.Show("BackgroundWorker " + genericlist[0]);

        }

【问题讨论】:

  • 听起来很合理,如果你调用一个函数两次,它会做同样的事情两次。你的问题是什么?为什么你认为这种行为是不正确的?
  • 如果你调用函数两次,它会执行两次代码。你还期待什么?
  • 我只点击一次按钮,为什么它会执行两次
  • 友情警告:不要在“DoWork”事件中访问 UI 组件。
  • @pst,对不起,我不明白

标签: c#


【解决方案1】:

我怀疑 多个 DoWork 事件被无意中添加了。

也就是说,每次调用start 方法都会注册一个 DoWork 事件处理程序。这添加并且不替换现有处理程序DoWork处理程序。那么接下来会有多个 DoWork 处理程序被调用.. 1、2、3 等等。

// creates a NEW delegate and adds a NEW handler
bw.DoWork += (obj, e) => bw_DoWork(str, strb);

我建议不要在这里使用闭包,而只是使用方法组(隐式转换为委托),然后将数据传递给RunWorkerAsync 调用(有一个表单接受数据的参数)。

RunWorkerCompleted += 行不存在此问题,因为它传递了来自方法组的委托(保证始终评估为相同的委托对象1)。因此,对该行重复的+= 调用替换处理程序。


例子:

class MyData {
   public string StrA { get; set; }
}

// These only need to be setup once (and should be for clarity).
// However it will be "ok" now if they are called multiple times
// as, since the delegates are the same, the += will
// act as a replacement (as it replaces the previous delegate with itself).
bw.WorkerSupportsCancellation = true;
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;

// Pass data via argument
bw.RunWorkerAsync(new MyData {
    StrA = str,
});

void bw_DoWork (object sender, DoWorkEventArgs e) {
    var data = (MyData)e.Argument;
    var str = data.StrA;
    // stuff
}

1 我不确定它是否保证引用相等,但是使用这种方法可以稳定地从方法组的代表调用+=-=如果通过new DelegateType(MethodGroup)获得。

写。我在主帖中的评论:如果 UI 元素是从创建它们的线程访问的,那么将会有有趣的“跨线程操作异常”。我相信消息框的这种用法是“可以的”(当不是由另一个线程的所有者创建时),但是在 BackgroundWorker 的 DoWork 中访问 UI 的做法通常是可疑的。


另外,不要在这里打电话给bw.Dispose();将其与拥有的容器或上下文一起处理。在这种情况下,它似乎是好的和良性的,但只有在该 BGW 实例将永远再次使用时才这样做。从事件处理程序调用它也是可疑的,因为 BGW 仍然是“活动的”。

【讨论】:

  • @Power-Mosfet 现在,我确实期望上述建议会发生 :) 澄清一下,DoWork 被称为 multiple 每个点击事件发生的次数
  • @Power-Mosfet 我上面的修改应该确保它不会发生。确保添加DoWork 处理程序的only 行是bw.DoWork += bw_DoWork;,并且not 另一个DoWork += .. 隐藏在某个棘手的地方。如果进行此修改,则DoWord+= 也可以(并且可能应该)移动到test 类构造函数。还要确保项目已成功重建并且正在运行正确的可执行文件——这可能是一个棘手的问题!另请参阅我稍后的关于(不)在此处使用Dispose() ..
  • 我创建了一个新的类复制粘贴您的代码快照,成功重建项目并且仍然是相同的效果。我还删除了 Dispose()
  • @Power-Mosfet - 您是否只是将整个代码复制并粘贴到 Start 方法中?您是否阅读并关注此答案中的// cmets?
【解决方案2】:

我遇到了与上述评论者“Power-Mosfet”相同的问题

最后,添加一个new BackgroundWorker() 然后分配给全局 bw 值将解决我的问题。

代码是,改自:

private BackgroundWorker gBgwDownload;

private void yourFunction_bw(xxx)
{
    // Create a background thread
    gBgwDownload.DoWork += bgwDownload_DoWork;
    gBgwDownload.RunWorkerCompleted += bgwDownload_RunWorkerCompleted;
    //omited some code
    gBgwDownload.RunWorkerAsync(paraObj);
}

到:

private BackgroundWorker gBgwDownload;

private void yourFunction_bw(xxx)
{
    // Create a background thread
    gBgwDownload = new BackgroundWorker(); /* added this line will fix problem */
    gBgwDownload.DoWork += bgwDownload_DoWork;
    gBgwDownload.RunWorkerCompleted += bgwDownload_RunWorkerCompleted;
    //omited some code
    gBgwDownload.RunWorkerAsync(paraObj);

}

【讨论】:

  • crifan 你是个英雄,这也解决了我的问题。
【解决方案3】:

还有另一个原因。在其生成的代码中查找DoWorkEventHandler InitializeComponent() 如果您已通过组件 UI 属性生成它并自己注册。

因为如果您再次注册它,它不会覆盖前一个,而是会添加另一个事件并调用两次。

【讨论】:

    【解决方案4】:

    在我的例子中,BackgroundWorker 运行了两次,因为我在表单的构造函数类中声明了 DoWork、ProgressChanged 和 RunWorkerCompleted 事件处理程序,但 Visual Studio 2013 已经在此表单类的设计器部分声明了它。

    所以,我刚刚删除了我的声明,它运行良好。

    【讨论】:

    • 对我来说也是一样,我从互联网上复制粘贴了“addHandler”行,这进行了多次调用。删除这些行就可以了
    【解决方案5】:

    谢谢....这段代码运行良好...为 backroundworker 创建新实例是个好主意...。 现在我们可以在for/while循环中调用这个函数,并且可以运行多个backgroundworker进程。

    我是这样编码的 当按钮单击完成时.. 不干扰主线程流......多个进程将在后台运行...... 我只是使用消息框弹出..但是我们可以在“bgwDownload_DoWork”函数中运行计时过程......并且将创建多个进程......我们不需要检查BackgroundWorker是否忙碌......

    private void button1_Click(object sender, EventArgs e)
    {
       for (int i = 0; i < 3; i++)
         yourFunction_bw(i);
    
     }
    private BackgroundWorker gBgwDownload;
    
    private void yourFunction_bw(int i)
    {
        // Create a background thread
        gBgwDownload = new BackgroundWorker(); // added this line will fix problem 
        gBgwDownload.DoWork += bgwDownload_DoWork;
        gBgwDownload.RunWorkerAsync(i);
    
    }
    
    private void bgwDownload_DoWork(object sender, DoWorkEventArgs e)
    {
      int stre = (int)e.Argument;
      MessageBox.Show(stre.ToString ()); // time taken process can be added here
    }
    

    【讨论】:

      【解决方案6】:

      我今天遇到了这个问题,当我注意到每次显示表单时,后台工作人员 RunWorkerCompleted 事件被多次调用时,我将一个后台工作人员放在一个正在执行长时间运行任务的弹出表单上。

      我的问题是我在关闭表单后没有处理它,这意味着每次我显示表单时,它每次都会添加另一个处理程序。

      完成后处理表单解决了我的问题。只是想在这里提一下,因为我在为我的情况寻找解决方案时遇到了这个页面。

      【讨论】:

        【解决方案7】:

        我从设计器中移除了控件,并在代码中实例化了一个新的 WorkerProcess:

        示例: var bwProcess = new BackgroundWorker();

        bwProcess.DoWork += new DoWorkEventHandler(bwProcess_DoWork);

        bwProcess.RunWorkerCompleted += bwProcess_RunWorkerCompleted;

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-01-19
          • 1970-01-01
          • 2021-01-03
          • 1970-01-01
          • 2021-03-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多