你如何在RunWorkerCompletedEventArgs对象中设置UserState?

14
大家好。 我有一个 BackgroundWorker 对象数组,它们运行 Worker 类的实例。当我调用 Worker 类时,对象实例会执行其操作,然后完成代码(循环结束)。我能够监听到 RunWorkerCompleted() 事件,但是当它调用我设置的委托时,我需要知道哪个 Worker 对象刚刚完成了执行。
我在 RunWorkerCompletedEventArgs 中看到 UserState 属性,但是我不知道如何在我的 Worker 对象中设置它,因为它正在完成操作。
有任何想法吗?
来自我 WorkManager.cs 类的片段
public Worker AddWorker()
{
    Worker w = new Worker();

    _workers.Add(w.WorkerID,w);

    BackgroundWorker bg = new BackgroundWorker();
    _bgworkers.Add(bg);

    bg.DoWork += w.Start;
    bg.WorkerReportsProgress = true;
    bg.WorkerSupportsCancellation = true;
    bg.ProgressChanged += ProcessWorkerMessage;
    bg.RunWorkerCompleted += WorkerFinished;


    w.WorkManager = this;
    w.BackgroundWorker = bg;

    bg.RunWorkerAsync(w);


    return w;

}


public void WorkerFinished(object sender, RunWorkerCompletedEventArgs e)
{
    if (_onManagerEvent != null)
        _onManagerEvent(new ManagerEvent { EventDate = DateTime.Now, Message = "Worker ??? successfully ended." });
}

当我的Worker对象完成其Start()方法中的循环时,我该如何填充传递给我的WorkerFinished方法的RunWorkerCompleteEventArgs对象“e”的userState属性?

谢谢。

3个回答

15

你的Worker类中的Start方法可以为DoWorkEventArgs参数设置Result属性。以下是一个示例:

void Start(object sender, DoWorkEventArgs e)
{
   //Do your loop and other work.
   e.Result = this;
}

然后在完成事件处理程序中,您可以检索e.Result:

public void WorkerFinished(object sender, RunWorkerCompletedEventArgs e)
{
    //You should always check e.Cancelled and e.Error before checking e.Result!
    // ... even though I'm skipping that here

    Worker w = e.Result as Worker;
    if( w != null)
    {
        if (_onManagerEvent != null)
            _onManagerEvent(new ManagerEvent 
                    { 
                      EventDate = DateTime.Now, 
                      Message = String.Format("Worker {0} successfully ended."
                                              , w.ToString()) 
                    });
    }
}

谢谢Philip,这解决了问题。你提到始终检查e.Cancelled的评论是关键。我实际上是通过调用BackgroundWorker对象的CancelAsync()方法来停止我的工作线程。这导致RunWorkercompletedEventArgs结果属性没有我的原始对象。我已经重构了代码,通过在工作对象上设置一个属性(以线程安全的方式!)来停止工作,然后在我的工作代码循环中仅检查该属性。如果我看到信号,我就退出循环,然后RunWorkercompletedEventArgs参数中的e.Result就有我的对象了。 - sisdog

4

UserStateBackgroundWorker 中已知的一个 bug:

http://www.pluralsight-training.net/community/blogs/mike/archive/2005/10/21/15783.aspx(原始链接已失效)

我在过去遇到这种情况时,通常会使用 RunWorkerCompletedEventArgs.Result(正如 Philip 建议的那样),或者如果可能的话,让我的工作线程从 BackgroundWorker 派生(然后我可以添加尽可能多的额外状态,并将整个工作线程作为发送方参数传递给 BackgroundWorker 引发的事件,同时仍然能够使用 Result 以其预期目的为其服务)。


我没有考虑继承后台工作器。很好的添加。 - Keith Aymar
阅读您发布的链接,日期显示这个漏洞已经存在了15年! - Kit

1
十五年后,即使微软将Winforms移植到.NET Core,lesscode答案中提到的错误仍未被修复。我还有一个额外的要求,那就是当工作线程被取消时需要获取“用户对象”。
由于无法设置该用户对象,也无法在工作线程被取消时访问结果,因此我不得不单独跟踪取消操作,而不是使用RunWorkerCompletedEventArgs 实现。
以下是我的解决方案。首先,创建一个结果对象,它可以同时扮演双重角色(抱歉SRP),作为一个用户对象,类似于:
class WorkerStateAndResult
{
    public bool Errored { get; set; }
    public bool Canceled { get; set; }
    // other state/results...
}

然后,在您的工作者处理程序中,立即将Result属性设置为WorkerStateAndResult的实例。在处理程序内部,当工作者被取消时,您不会将DoWorkEventArgs.Canceled设置为true;相反,您将在状态对象上设置该属性;错误情况也是如此。您的处理程序最终看起来像:

private void HandleWorkerDoWork(object sender, DoWorkEventArgs e)
{
    var stateAndResult = new WorkerStateAndResult(...);
    e.Result = stateAndResult;

    try
    {
        // do the work and check for cancellation. if cancellation happens, set Canceled
        // instead of using built-in e.Canceled property
        stateAndResult.Canceled = ResultOfActualWork();
    }
    catch
    {
        // handle errors, again, not using built-in mechanism
        stateAndResult.Error = true;
    }
    finally
    {
        // any cleanup
    }
}

最后,在 RunWorkerCompleted 处理程序中,您可以访问结果(即包含所有结果和状态的变量),并检查 ErrorCanceled 属性,以执行必要的逻辑。
private void HandleAnalyzerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // this would normally throw if error or canceled, but not anymore!
    var result = (WorkerStateAndResult)e.Result;

    if (result.Canceled)
    {
        // do canceled logic
    }
    else if (result.Errored)
    {
        // do error logic
    }
    else
        // do regular logic using result
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接