ProgressChanged 回调上的 BackgroundWorker 和 UserState 问题
看来我不理解后台工作者中用户状态的概念。 我正在处理的应用程序中遇到问题,我需要解释为什么会发生我没有预料到的事情。
我构建了一个演示应用程序来更简单地重现该问题:
public class Tester
{
private BackgroundWorker _worker = new BackgroundWorker();
public void performTest()
{
Tester tester = new Tester();
tester.crunchSomeNumbers((obj, arg) =>
{
WorkerArgument userState = arg.UserState as WorkerArgument;
Console.WriteLine(string.Format("Progress: {0}; Calculation result: {1}", arg.ProgressPercentage, userState.CalculationResult));
});
}
public void crunchSomeNumbers(Action<object,ProgressChangedEventArgs> onProgressChanged)
{
_worker.DoWork += new DoWorkEventHandler(worker_DoWork);
_worker.ProgressChanged += new ProgressChangedEventHandler(onProgressChanged);
_worker.WorkerReportsProgress = true;
_worker.RunWorkerAsync(new WorkerArgument { CalculationResult=-1, BaseNumber = 10 });
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
WorkerArgument arg = e.Argument as WorkerArgument;
for (int i = 0; i < 10; i++)
{
// calculate total with basenumber
double result = arg.BaseNumber * (i * 10);
arg.CalculationResult = result;
worker.ReportProgress(i * 10, arg);
}
}
public class WorkerArgument
{
public int BaseNumber { get; set; }
public double CalculationResult { get; set; }
}
}
如果您在控制台应用程序中运行此代码:
class Program
{
static void Main(string[] args)
{
Tester tester = new Tester();
tester.performTest();
Console.ReadLine();
}
}
结果如下:
我不明白的是为什么计算结果总是相同的,而你可以清楚地看到它在每次计算中运行时应该不同 DoWork 方法的 forloop。
It seems I don't understand the concept of userstate in a backgroundworker.
I am encountering a problem in an application I'm working on and I need an explanation for why something is happening that I didn't expect.
I have built a demo app to reproduce the issue more simply:
public class Tester
{
private BackgroundWorker _worker = new BackgroundWorker();
public void performTest()
{
Tester tester = new Tester();
tester.crunchSomeNumbers((obj, arg) =>
{
WorkerArgument userState = arg.UserState as WorkerArgument;
Console.WriteLine(string.Format("Progress: {0}; Calculation result: {1}", arg.ProgressPercentage, userState.CalculationResult));
});
}
public void crunchSomeNumbers(Action<object,ProgressChangedEventArgs> onProgressChanged)
{
_worker.DoWork += new DoWorkEventHandler(worker_DoWork);
_worker.ProgressChanged += new ProgressChangedEventHandler(onProgressChanged);
_worker.WorkerReportsProgress = true;
_worker.RunWorkerAsync(new WorkerArgument { CalculationResult=-1, BaseNumber = 10 });
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
WorkerArgument arg = e.Argument as WorkerArgument;
for (int i = 0; i < 10; i++)
{
// calculate total with basenumber
double result = arg.BaseNumber * (i * 10);
arg.CalculationResult = result;
worker.ReportProgress(i * 10, arg);
}
}
public class WorkerArgument
{
public int BaseNumber { get; set; }
public double CalculationResult { get; set; }
}
}
If you run this code in a console App:
class Program
{
static void Main(string[] args)
{
Tester tester = new Tester();
tester.performTest();
Console.ReadLine();
}
}
This is the result:
What I don't understand is why the calculation result is always the same while you can clearly see that it should be different on each calculation run in the forloop of the DoWork method.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
更多
发布评论
评论(4)
您假设事件在循环的下一次迭代之前引发并执行。不幸的是,这是不正确的。
发生的情况是,您的 for 循环在第一个事件执行之前完成。因此,在调用 Console.WriteLine 之前,userState.CalculationResult 位于 900。如果将 for top 更改为
您应该会看到数量有所增加,但在所有事件执行完毕之前它就达到了最大数量。
另一种方法是在调用worker.ReportProgress之前放置一个Console.WriteLine。您将看到 for 循环的完成顺序与事件报告不同。它在第一个事件代码输出上不会那么完整,因为 Console.WriteLine 是一个非常慢的调用,并且大大减慢了 for 循环的执行速度。
多线程需要记住的令人兴奋的问题之一是调用事件是非阻塞的。
You assumption is that the events are raised and executed before the next iteration of the loop. Unfortunately this is not correct.
What is happening is that your for loop is completing before the first event is executed. So userState.CalculationResult is at 900 before the call to Console.WriteLine. You if you change for for top to
You should see that there is an increase in numbers, but it gets to the maximum number before all the events have executed.
Another way is to put a Console.WriteLine before the call to worker.ReportProgress. You will see the order of completion of the for loop being different from the event report. It won't be as complete on the first event code output as the Console.WriteLine is a really slow call and massively slows down the execution of the for loop.
One of the exciting issues to remember with multi threading is calling events is non blocking.
有两个问题:
1)循环在第一个事件处理程序执行之前执行
如果在调用 ReportProgress 后冻结后台工作线程执行,您可以看到它。
2) 您正在使用 WorkerArgument 的一个实例 - 因此,当执行事件处理程序时,它具有当前的 WorkerArgument 值,而不是引发事件时的值。当您引发事件时,也可以看到这只是传递新的参数实例。
There are two problems:
1) Loop is executed before first event handler executed
You can see it, if you freeze background worker thread execution after call to ReportProgress.
2) You are using one instance of WorkerArgument - so, when event handler is executed it has current WorkerArgument value, not that one which was when event was raised. Too see this just pass new instance of argument when you raise events.
我无法直接发现问题,但这可能与匿名方法和变量固定有关。尝试通过将每个匿名方法或 lambda 重写为正确的方法来缩小范围,并查看问题是否仍然存在。
I can't spot the problem outright, but it's probably about anonymous methods and variable pinning. Try narrowing it down by rewriting each anonymous method or lambda as a proper method and see if the problem persists.
您需要调用传入的工作人员:
You'll want to call the worker that was passed in: