2

我遇到了使用 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 次时,我不想使用 cpu 每秒循环 1000 次。*是否需要控制 GUI 的更新速率?
4

2 回答 2

3

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

是的,非常如此。它可以帮助您处理无法从工作线程更新 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.Sleep() 人为地减慢它的速度。这不是一件常见的事情,启动工作线程的目的是做工作。休眠的线程以非生产方式使用昂贵的资源。

可以更具体地控制循环在设定周期内循环的次数吗?当我只需要每秒循环 30 次时,我不想使用 cpu 每秒循环 1000 次。

和上面一样,你必须睡觉。通过执行循环 30 次然后休眠一秒钟来做到这一点。

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

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

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

于 2012-12-22T12:59:20.333 回答
1

我将尝试逐个问题地回答你

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

请参阅有关BackgroundWorkerThread类的 MSDN 文档

于 2012-12-22T09:36:12.020 回答