似乎我不理解后台工作者中的用户概念。 我正在处理我正在处理的应用程序中的问题,我需要解释为什么发生了一些我没想到的事情。
我已经构建了一个演示应用程序来更简单地重现问题:
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; }
}
}
如果您在控制台App中运行此代码:
class Program
{
static void Main(string[] args)
{
Tester tester = new Tester();
tester.performTest();
Console.ReadLine();
}
}
结果如下:
我不明白为什么计算结果总是一样的,你可以清楚地看到在DoWork方法的forloop中每次计算运行时它应该是不同的。
答案 0 :(得分:2)
您假设事件是在循环的下一次迭代之前引发并执行的。不幸的是,这是不正确的。
正在发生的事情是你的for循环在第一个事件执行之前完成。因此,在调用Console.WriteLine之前,userState.CalculationResult位于900。如果您将顶部改为
,则为for (int i = 0; < 1000000; i++)
您应该看到数字有所增加,但在所有事件执行之前它会达到最大数量。
另一种方法是在调用worker.ReportProgress之前放入Console.WriteLine。您将看到for循环的完成顺序与事件报告不同。它不会像第一个事件代码输出那样完整,因为Console.WriteLine是一个非常慢的调用,并且大大减慢了for循环的执行速度。
多线程要记住的一个令人兴奋的问题是调用事件是非阻塞的。
答案 1 :(得分:1)
有两个问题:
1)循环在第一个事件处理程序执行之前执行 如果在调用ReportProgress后冻结后台工作线程执行,则可以看到它。
arg.CalculationResult = result;
worker.ReportProgress(i * 10, arg);
Thread.Sleep(500);
2)您正在使用WorkerArgument的一个实例 - 因此,当执行事件处理程序时,它具有当前的WorkerArgument值,而不是引发事件时的值。在你举起事件时,太看到这只是传递新的参数实例。
worker.ReportProgress(i * 10, new WorkerArgument(){ CalculationResult = result });
答案 2 :(得分:0)
我无法彻底发现问题,但可能是关于匿名方法和变量固定。尝试通过重写每个匿名方法或lambda作为正确的方法来缩小范围,并查看问题是否仍然存在。
答案 3 :(得分:0)
您需要调用传入的工作人员:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker this_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;
this_worker.ReportProgress(i * 10, arg);
}
}