C# (C) BackgroundWorker()进程已更改,无法工作

C# (C) BackgroundWorker()进程已更改,无法工作,c#,wpf,multithreading,backgroundworker,C#,Wpf,Multithreading,Backgroundworker,我有一个WPF应用程序,它由两个线程组成,模拟企业在52周内生产和销售商品,每周只允许一次交易。我还需要使用后台工作程序,以便在listview中显示数据。现在,我的UI在单击simulate时冻结,但我可以看到输出仍在调试终端中工作。我已经尝试了我能想到的一切,老实说,我得到了老师的帮助,甚至他也找不到有效的解决办法 当我调用Simulate时冻结我的UI是什么? 当我的代码不同并且我的UI没有冻结时,我的listview永远不会更新,因为DataProgress似乎不起作用-e.UserSt

我有一个WPF应用程序,它由两个线程组成,模拟企业在52周内生产和销售商品,每周只允许一次交易。我还需要使用后台工作程序,以便在listview中显示数据。现在,我的UI在单击simulate时冻结,但我可以看到输出仍在调试终端中工作。我已经尝试了我能想到的一切,老实说,我得到了老师的帮助,甚至他也找不到有效的解决办法

当我调用Simulate时冻结我的UI是什么? 当我的代码不同并且我的UI没有冻结时,我的listview永远不会更新,因为DataProgress似乎不起作用-e.UserStart永远不会迭代。 模拟按钮调用:

私有void模拟对象发送方,RoutedEventArgs e { //声明BackgroundWorker 数据=新的可观察收集; 工人=新的背景工人; worker.WorkerReportsProgress=true; worker.worker支持扫描单元=true; worker.RunWorkerAsync52; worker.DoWork+=ShowData; worker.ProgressChanged+=数据进度; worker.RunWorkerCompleted+=DataToDB; 生产=新产品QTYProduction,timeExecProd; 销售=新的SalesqtySales,timeExecSales; 螺纹产品=新螺纹产品。产品; 生产启动; 线程100; 线程销售=新线程销售。销售; 销售。启动; } 道工:展示数据:

Console.WriteLineSimulation启动|初始库存:500; 生产=新产品QTYProduction,timeExecProd; 销售=新的SalesqtySales,timeExecSales; 而工厂每周 如果e.UserState!=null//在使用调试器时,它看起来像是调用了over&over { 数据。添加新操作 { id=rnd.NEXT1999, name=Factory.name, qtyStock=工厂库存, averageStock=Factory.averageStock, 周=工厂。周 }; listview.ItemsSource=数据; } RunWorkerCompleted:DataToDB:

//输出目前完成的工作。 如果您想知道当我调用线程时会发生什么,它看起来是这样的:

出售:


虽然Factory.Week一方面,在发布的代码中没有足够的上下文来完整地回答您的问题。然而,我们可以从您发布的代码中推断出哪里出了问题

首先,让我们试着回答你的两个问题。我们可能会推断出以下情况:

此代码如下:

if (e.UserState != null) 
{
    Data.Add(new Operations() 
    {
        id = rnd.Next(1,999),
        name = Factory.name,
        qtyStock = Factory.Stock,
        averageStock = Factory.AverageStock,
        week = Factory.Week
    });
    listview.ItemsSource = Data;
}
您正在使用Windows窗体后台线程对象来尝试和更新WPF GUI对象,该对象应仅在主GUI线程上完成。还有一个明显的问题,就是永远不要从非UI线程更新GUI对象。使用BackgroundWorker在前台/后台线程、上下文和执行方面也有自己的问题,因为它依赖Dispatcher和SynchronizationContext来完成任务

然后在这一行中反复设置绑定是一种好奇:

listview.ItemsSource=数据

让我们在里面插一根别针一会儿

正如另一位评论者已经指出的,while循环中没有退出策略:

while (Factory.Week < max) // max = 52 
{
    if (worker.CancellationPending) // also this isn't reacting to worker.CancelAsync();
        e.Cancel = true;

   // My teacher tried to call my threads from here, but it breaks the purpose of having 
   // two threads as he was just calling 52 times two functions back to back and therefore
   // wasn't "randomizing" the transactions.

    int progressPercentage = Convert.ToInt32(((double)(Factory.Week) / max) * 100);
    (sender as BackgroundWorker).ReportProgress(progressPercentage, Factory.Week);
}
但这不是更大的问题。。。除了误用/误解何时/多少/如何使用线程之外,似乎没有任何类型的线程同步。用这种方法无法预测或跟踪生命周期的线程执行

在这一点上,这个问题在技术上或多或少得到了回答,但我觉得这只会让你更加沮丧,也不会比你开始时更好。所以,也许一个基础设计速成班可以帮助你理顺这个烂摊子,这是你的老师应该做的

假设您正在进行软件开发,并且既然您在这里选择了WPF作为您的试验板,那么您可能会遇到诸如MVC模型视图控制器或MVVM模型视图模型之类的术语。您还可能会遇到设计原则,如固体、关注点分离和将事物分组到服务中

这里的代码是所有这些框架和原则存在的完美例子。让我们看看您遇到的一些问题以及如何解决这些问题:

线程代码逻辑和服务-控制器[粗略地说]与表示代码listview更新-view和collection更新混合在一起 ble集合-模型。这就是为什么您在编码、修复和维护手头的问题时遇到如此困难的原因之一。要清理它,请将其与关注点分离。您甚至可以将每个操作移动到自己的类中,并使用该类服务/微服务的接口/API

并非所有问题都需要用线程来解决。但现在,让我们先学爬,然后在跑之前先走路。在您开始学习async/await或TPL任务并行库之前,让我们先从老一套做起。得到一本好书。。。20年前的一些东西被发现了。。。走老路,学习如何使用线程池和内核同步对象,如互斥体、事件等,以及如何在线程之间发送信号。一旦掌握了这一点,就可以学习TPL和async/await

不要越过小溪。不要混合使用WinForms、WPF和我甚至看到了Console.WriteLine

了解数据绑定,特别是它如何在WPF中工作。ObservaleCollection是您的朋友,将您的ItemsSource绑定到它一次,然后更新ObservaleCollection并让GUI对象单独使用

希望这将帮助您理顺代码并使其运行


祝你好运

如前所述,while循环从未终止。它以非常高的速率猛击UI线程,每秒数百万次。这也是非常昂贵的代码,它使UI线程消耗100%的核心,永远无法赶上。其副作用是,它不再能够承担起正常的职责,重新粉刷窗口并响应输入。考虑到这是多么错误,最好采取激烈的行动。而是在DoWork中生成一个结果列表,并使用RunWorkerCompleted更新ItemsSource。只需查看代码,不要在连接所有事件之前告诉后台工作人员开始运行。此外,仅更改控件上的ItemSource不会强制刷新。至少,您应该在listview上调用DataBind,并可能调用refresh/repaint。。将while Factory.Week