【问题标题】:Some questions on Multithreading and Background worker threads in windows formwindows窗体中多线程和后台工作线程的一些问题
【发布时间】:2012-12-22 08:33:31
【问题描述】:

我遇到了需要在使用 C++ 的 Windows 窗体 GUI 应用程序中使用多线程。从我对该主题的研究来看,后台工作线程似乎是我的目标。根据示例代码我有

System::Void backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) 
{
    BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);
    e->Result = SomeCPUHungryFunction( safe_cast<Int32>(e->Argument), worker, e );
}

但是有一些事情我需要弄清楚并弄清楚

  • 后台工作线程会让我的多线程生活更轻松吗?
  • 为什么我需要 e->Result?
  • 传递给 backgroundWorker1_DoWork 函数的参数是什么?
  • 参数safe_cast(e->Argument)的作用是什么?
  • 我应该在我的 CPUHungryFunction() 中做些什么?
  • 如果我的 CPUHungryFunction() 有一个无限循环的 while 循环怎么办?
  • 我是否可以控制工作线程获得的处理器时间?
  • 能否更具体地控制循环在设定周期内循环的次数?当我只需要每秒循环 30 次时,我不想使用 up cpu 每秒循环 1000 次。 *是否需要控制 GUI 的更新速度?

【问题讨论】:

  • 我刚刚在我的帖子中添加了另一个关于 safe_cast(e->Argument) 的快速问题。

标签: c++ winforms multithreading c++-cli backgroundworker


【解决方案1】:

后台工作线程会让我的多线程生活更轻松吗?

是的,非常如此。它可以帮助您处理无法从工作线程更新 UI 的事实。特别是 ProgressChanged 事件可以让您显示进度,而 RunWorkerCompleted 事件让您可以使用工作线程的结果来更新 UI,而无需处理跨线程问题。

为什么我需要 e->Result?

将您所做工作的结果传回给 UI 线程。您可以在 RunWorkerCompleted 事件处理程序 e->Result 属性中取回该值。然后使用结果更新 UI。

传入函数的参数是什么?

告诉工作线程做什么,是可选的。否则与将参数传递给任何方法相同,只是更尴尬,因为您无法选择参数。例如,您通常会从您的 UI 传递某种值,如果您需要传递多个值,请使用一个小助手类。总是倾向于这样做而不是尝试在工作人员中获取 UI 值,这非常麻烦。

我应该在我的 CPUHungryFunction() 中做些什么?

当然会消耗 CPU 周期。或者通常做一些需要很长时间的事情,比如 dbase 查询。这不会消耗 CPU 周期,但需要很长时间才能让 UI 线程在等待结果时死机。粗略地说,每当你需要做一些超过一秒的事情时,你应该在工作线程而不是 UI 线程上执行它。

如果我的 CPUHungryFunction() 有一个无限循环的 while 循环怎么办?

那么你的工人永远不会完成,也永远不会产生结果。这可能有用,但并不常见。您通常不会为此使用 BGW,只需将其 IsBackground 属性设置为 true 的常规 Thread。

我是否可以控制我的工作线程获得的处理器时间?

您可以通过调用 Thread.Sleep() 人为地减慢它的速度。这不是一件常见的事情,启动工作线程的目的是做工作。休眠的线程正在以非生产方式使用昂贵的资源。

能否更具体地控制循环在设定周期内循环的次数?当我只需要每秒循环 30 次时,我不想占用 CPU 每秒循环 1000 次。

同上,你得睡觉了。为此,执行循环 30 次,然后休眠一秒钟。

是否需要控制 GUI 的更新速率?

是的,这非常重要。 ReportProgress() 可以是一个消防水带,每秒生成数千次 UI 更新。当 UI 线程跟不上这个速度时,你很容易遇到这个问题。您会注意到,UI 线程不再处理其常规职责,例如绘制 UI 和响应输入。因为它必须处理另一个调用请求才能运行 ProgressChanged 事件处理程序。副作用是 UI 看起来冻结,你得到了你试图通过工作人员解决的确切问题。它实际上并没有被冻结,它只是看起来那样,它仍在运行事件处理程序。但您的用户不会看到差异。

要记住的一件事是,ReportProgress() 只需要让人类的眼睛保持快乐。它看不到每秒发生 20 次以上的更新。除此之外,它只会变成难以理解的模糊。因此,不要将时间浪费在无论如何都没有用的 UI 更新上。您也将自动避免消防软管问题。调整更新率是你必须编程的事情,它不是内置在 BGW 中的。

【讨论】:

  • 谢谢汉斯,这些答案真的很有帮助。
【解决方案2】:

我会逐个问题回答你

  1. 是的
  2. DoWork 是一个 void 方法(并且必须如此)。 DoWork 也执行 在与调用线程不同的线程中,因此您需要有一个 将某些内容返回给调用线程的方法。 e->结果 参数会传递给RunWorkerCompleted里面的事件 RunWorkerCompletedEventArgs
  3. sender 参数是您可以使用的后台工作程序本身 为 UI 线程引发事件,DoWorkEventArgs 最终 包含从调用线程传递的参数(具有 叫RunWorkerAsync(Object))
  4. 无论您需要做什么。关注用户界面 DoWork 线程无法访问的元素。通常,一个 计算已完成工作的百分比并更新 UI(进度 栏或类似的东西)并调用 ReportProgress 与 用户界面线程。 (需要将WorkerReportProgress 属性设置为 是的)
  5. 没有什么是无限期运行的。您可以随时拔下电源线。 说真的,它只是另一个线程,操作系统会处理它并 当您的应用结束时销毁所有内容。
  6. 不知道你的意思是什么,但它可能是相关的 到下一个问题
  7. 您可以使用 Thread.Sleep 或 Thread.Join 方法来释放 一个循环后的 CPU 时间。睡觉的确切时间应该没问题 调整取决于你在做什么,当前的工作量 系统和处理器的原始速度

请参阅 BackgroundWorkerThread 类的 MSDN 文档

【讨论】:

  • +1 用于回答所有问题,但也许您应该因为仅提及 Thread.Join 就获得反对票 :)
  • @MartinJames,你能指出一些关于 Thread.Join 缺点的讨论吗?真的没用过,只是参考了看 MSDN 上关于消息泵的注释。
  • 谢谢史蒂夫,这些答案正是我正在寻找的答案。
猜你喜欢
  • 1970-01-01
  • 2011-06-18
  • 1970-01-01
  • 1970-01-01
  • 2014-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多