【问题标题】:BackgroundWorker problems on exit退出时的BackgroundWorker问题
【发布时间】:2014-07-29 17:08:06
【问题描述】:

我在这里遇到了一个难题,想知道一些事情:

  1. 我做错了吗?
  2. 后台工作人员在不同场景中的预期行为是什么...
  3. 如果可能,请回答为什么我会出现特定行为会很好...

对于第 1 点,以及最终的第 3 点,我将解释我在伪代码中所做的事情,以便您了解详细信息,而无需实际吐出数千行代码。当我写这篇文章时,我会查看代码本身,以确保信息在何时何地发生时是准确的。最后,我还将详细说明发生了什么以及我遇到问题的原因。

伪代码详情:

我有一个主 UI 线程(WinForms 表单),在选择几个配置选项后,您单击一个按钮。

此按钮的事件会在内存和文件系统上进行一些初步设置工作,以使事情顺利进行,一旦完成,就会触发一个后台工作程序。此后台工作人员初始化 5 个其他后台工作人员(表单范围变量),将它们的“完成”标志(布尔 - 相同范围)设置为 true,将它们的“日志”变量设置为新的 List<LogEntry>(相同范围),一旦完成,就会调用一个方法叫CheckEndConditions。此方法调用在初始后台工作程序的DoWork() 内完成,而不是在RunWorkerCompleted 事件中完成。

CheckEndConditions 方法执行以下逻辑:

  1. 如果所有“完成”变量都设置为 True...
  2. 获取所有 5 个 BW 的“日志”变量并将其内容添加到主日志。
  3. 将所有 5 个 BW 的“日志”变量重置为新的 List<LogEntry>
  4. 将所有 5 个 BW 的“完成”变量重置为 False。
  5. 调用MoveToNextStep() 方法,该方法返回一个Enum 值,代表下一步执行
  6. 根据(5)的结果,抓取一个需要处理的List<ActionFileAction>
  7. 检查以确保 (6) 有要执行的操作
  8. 如果否,将所有“完成”标志设置为 true,并调用自身以进入下一步...
  9. 如果是,则将此操作列表划分为 5 个列表,并将它们放入名为 ThreadActionSets[]List<ActionFileAction> 数组中
  10. 检查每个分区列表的内容,如果没有,则将相应线程的“完成”标志设置为 true(这样可以确保没有“结束竞争场景”)
  11. 使用 RunWorkerAsync() 关闭所有 5 个线程(当然,除非我们处于 Finished 步骤)
  12. Return

每个BW都有完全相同的DoWork()代码,基本上可以归结为以下几点:

  1. 我有什么要执行的操作吗?
  2. 如果否,请将我的e.Result var 设置为一个空的日志条目列表并退出。
  3. 如果是,则为集合中的每个动作循环并执行下面的 4-5-6...
  4. 我在做什么行动? (组、模块等)
  5. 基于 (4),我正在执行什么类型的操作? (添加、删除、修改)
  6. 根据 (5),执行正确的操作并在本地记录您所做的一切
  7. 完成所有操作后,将我的e.Result var 设置为“我所做的一切的日志”,然后退出。

每个BW都有相同的RunWorkerCompleted()代码,基本上可以归结为:

试试

  1. e.Result 变量中,获取List<LogEntry> 并将其放入我各自线程的“日志”变量中。
  2. 将我各自的“完成”变量设置为 true
  3. 致电CheckEndConditions()

捕捉

  1. 将我各自的“完成”变量设置为 true
  2. 致电CheckEndConditions()

基本上就是这样……总而言之,我将大量操作分成 5 个分区,然后将它们发送到 5 个线程,以比单个线程更快的速度执行它们。

问题

我遇到的问题是,我经常发现自己,无论我为比赛场景(特别是结束场景)投入了多少想法,都会遇到卡住/无响应的程序。

一开始,我的代码设置效率低下,问题出在 End Race Scenarios 上,线程完成得如此之快,以至于对 CheckEndConditions 的最后一次调用看到其中一个“Done”变量仍然设置为 false,实际上它没有/它已经完成......所以我将我的代码更改为您在上面看到的,我认为可以解决问题,但它没有。整个过程仍然卡住/进入睡眠状态,当发生这种情况时,实际上没有线程正在运行任何处理,这意味着最后一次调用 CheckEndConditions 时出现了问题(我认为,不确定)。

所以我的第一个问题:我做错了吗?做我想做的事的标准方法是什么?我所做的事情的逻辑对我来说是合理的,但它的表现并不像我期望的那样,所以也许逻辑不合理? ...

第二个问题:当这种情况发生时,BW 的预期行为是什么: 在未捕获的DoWork() 方法中发生错误......它会触发RunWorkerCompleted() 事件吗?如果没有,会发生什么?

第三个问题:有没有人看到我的问题出现的明显原因?

感谢您的帮助!

【问题讨论】:

  • 调试时挂在哪里? (您可以暂停调试以准确查看哪个线程在哪里)
  • 就是这样......我有代码在 UI 运行时更新它,所以我等待 UI 停止更新/停留在一个位置一段时间,所以我暂停调试器,然后我知道 Done1、2、3、4、5 都设置为 True,但什么都没有发生......几乎就像 CheckEndConditions 没有在某个点或什么地方被调用...... :( 如果我在 RunWorkerCompleted 中设置断点,它永远不会卡住...
  • 那么工作线程挂在哪里?你不能发布一些代码吗?
  • 你要学会害怕穿线兽,这样的黑森虫很常见。 bool 不是正确的同步对象,并且 像您描述的那样发生故障。正确的代码使用 WaitHandle.WaitAll() 或 Task.WaitAll()。任务应该是您的偏好,BackgroundWorker 不是完成这项工作的合适工具。
  • @MaxOvrdrv RunWorkerCompleted 事件不一定会在创建它的同一线程上引发(除非它是在 UI 线程上创建的)参见stackoverflow.com/questions/2806814/…

标签: c# multithreading backgroundworker


【解决方案1】:

根据 OP 的要求重新发布我的评论作为答案:

RunWorkerCompleted 事件不一定会在创建它的同一线程上引发(除非它是在 UI 线程上创建的)参见 BackgroundWorker RunWorkerCompleted Event

查看 OP cmets 了解更多详情。

【讨论】:

  • 谢谢。我能够将原始的“Spawn”调用移动到主 UI 线程,从而消除了任何 End Race 场景并阻止了卡纸的发生。
猜你喜欢
  • 2021-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多