【问题标题】:Mono's BackgroundWorker not working in background?Mono 的 BackgroundWorker 不在后台工作?
【发布时间】:2012-05-04 00:57:59
【问题描述】:

我遇到了 Mono 的困境。我试图了解BackgroundWorker 实例如何或为什么不能同时运行,或者至少不能非常有效或快速地运行。好像线程的数量受到了某种限制,但我不是 Mono 开发人员或 .NET 开发人员,所以我把自己交给了我的兄弟们的怜悯。

考虑以下示例:

using System;
using System.ComponentModel;
using System.Threading;

namespace backgroundworkertest
{
  class MainClass
  {
    public static void foo(object sender, DoWorkEventArgs e) {
      Console.WriteLine("Start");
      Thread.Sleep(500);
      Console.WriteLine("Done");
    }

    public static void Main (string[] args)
    {
      for(int i = 0; i < 6; i++) {
        BackgroundWorker b = new BackgroundWorker();
        b.WorkerReportsProgress = false;
        b.DoWork += new DoWorkEventHandler(foo);
        b.RunWorkerAsync();
      }

      Thread.Sleep(2000);
      Console.WriteLine("Really done");
    }
  }
}

当我运行上述程序时,我得到以下输出。

开始 开始 完毕 开始 开始 完毕 开始 完毕 真的完成了

首先,并非所有BackgroundWorkers 都在整整两秒内完成。从循环中,很明显我正在实例化 6 个工作人员,但只有 3 个打印“完成”。如果所有 6 人同时运行,我觉得两秒钟应该足以让这些工作人员完成。

真正的内幕是我有一个运行良好的应用程序(使用大量 BackgroundWorkers 在后台通过 HTTP 获取图像。但是,不同之处在于这个应用程序是在 C# .NET 中实现的. 当我将应用程序移植到 Mono 时,BackgroundWorkers 似乎一次只运行几个,因此 HTTP 连接很容易超时。我想看看是否是这种情况,因此上面的例子和问题...

这里发生了什么?为什么不是所有的BackgroundWorkers 都同时运行?我需要设置一些参数吗?

【问题讨论】:

  • 在有人问之前,我知道我应该使用线程池。 ;)

标签: c# .net multithreading mono backgroundworker


【解决方案1】:

我不能 100% 确定 Mono 的实现,但我对 Microsoft 版本非常了解,我只能假设 Mono 的行为方式相同或相似。

首先,澄清一下,BackgroundWorker确实,事实上,生成 .NET 线程(不同于本机线程),它与您自己调用 ThreadPool.QueueUserWorkItem 是一样的。这是一个很小的细节,可以帮助您了解为什么会看到这种行为。重要的是要记住 .NET 中的线程实际上可能并不代表本机线程。实际上,所有 6 个后台工作人员在 6 个 .NET 线程上运行时,都可以在 1-2 个本机线程上运行。在我的机器上,上面的代码实际上只产生了 3 个 native 线程。

其次,您没有考虑构建新的BackgroundWorker、设置委托、然后异步运行工作线程所需的时间,这可能涉及也可能不涉及创建本机线程,这实际上是一个相当大的昂贵的操作。添加一个计时器,在我的测试中,创建全部六个总共只需要一秒钟多一点,有时会更长一些。由于调度,ThreadPool 可能需要几毫秒才能发现所有线程都已饱和/阻塞(因为Thread.Sleep),然后创建一个新线程。这意味着,在某些情况下,您的后台工作人员可能会在 0:01:700 左右开始,此时只剩下 300 毫秒,但函数会休眠 500 毫秒。没有足够的时间完成任务。

我不认为 Mono 的实现运行速度比 Microsoft 慢一点是没有争议的,我敢肯定这就是为什么您可能没有发现您可以在它们的运行时发生相同行为的原因。

如有疑问,请归咎于您自己的代码,因为这通常是问题所在。睡多少时间你期望完成一个操作,并不是测试某事的好方法。如果计算机上正在运行其他程序会怎样?如果其他进程的线程抢占了您的线程,Thread.Sleep 的运行速度不会变慢。在这种情况下看起来会更糟。

如果你在 foo 方法中将代码更改为休眠 5ms,并在创建后台工作人员后在 main 方法中等待 20ms,与你等待的比例相同,我可以看到以下内容:

开始 开始 完毕 完毕 开始 开始 真的做到了 按任意键继续 。 . .

只有两个完成了,甚至都没有开始。我希望你现在看到你的测试中的缺陷。它们同时运行,我猜你的问题可能出在其他地方。你说你在后台通过 http 获取图像。请记住,如果某些事情不能正常工作,我会说只有 1% 的时间实际上是运行时发生了一些奇怪的事情,另外 99% 的时间是你的代码。您使用的是同步 I/O 还是异步 I/O?只有一个线程处理请求?我会从那里开始。

编辑:

我决定看看 Mono 的实际性能如何,而不是仅仅依赖于我在 Microsoft 实现中发现的内容。在发现Mono实际上开始了所有6个后台工作之后;他们都在 2000 毫秒的限制内完成!为了记录,这里是结果的比较。我稍微修改了代码以显示发生了什么,但我向您保证,当我按原样运行您的代码时,我得到了相同的结果。 “Done Creating...”消息在主线程进入 2000 毫秒睡眠之前打印,其中打印的时间戳是后台工作人员进入其工作方法的时刻。

Windows 单声道 完成创建...完成创建... 00:00:00.0018610 00:00:00.0088775 00:00:00.0019544 开始 开始 00:00:00.5074494 开始 开始 完成 完成 完成 00:00:00.5615083 00:00:00.5027995 开始 开始完成 00:00:00.5028008 00:00:01.0082133 开始 开始 完成时间 00:00:01.0082900 完成 开始 00:00:01.0025428 完成 开始 00:00:01.0618140 00:00:01.0026164 开始 开始完成 完成 完成 完成 完成 真的做到了 真的做到了

我在两个完全相同的机器上进行了测试(Dell T3400,相同的硬件),一个是 Windows 7 x64,另一个是 Ubuntu 12.04 x64。没有不同。确保您使用可执行文件的 release 版本进行测试,而 没有附加任何调试器。还要确保您在相同的硬件上对其进行测试。有可能,但不太可能,这是您的 linux/mono 安装的问题(假设您在 linux 上运行 mono,而不是 windows)。就像我之前说的,我认为这些结果支持它,我认为这不是问题所在。我会在别处寻找,在你的代码中的某个地方。

【讨论】:

  • 只是提出一些问题。它不直接使用 QueueUserWorkItem,它使用 BeginInvoke 最终使用 ThreadPool。另外发生的事情是他达到了 ThreadPool 最小值。一旦达到最小值,它将在创建新线程之前等待一段时间。因此,将最小值设置为 6 将防止在这种情况下发生延迟。
  • @High828 - 是的,这不是我想要表达的意思,在说它调用 QueueUserWorkItem 时,我在想最简单的解释方法,我是想说类似于用户调用该方法。不过,就个人而言,我倾向于通过 CLR 遵循 C# 中的建议,因为几乎总是应该避免弄乱 ThreadPool 的线程操作方法。运行时通常比我更擅长确定线程性能。但是,我怀疑他在他的主应用程序中达到了 ThreadPool 的最小值,只是这个测试。我觉得他的问题出在其他地方。
  • 嗯,从我的测试来看,似乎是这样。线程最小值对我来说是 4 个,我一次得到一批 4 个,然后慢慢地 2 个。当我将最小值提高到 6 个时,我让它们同时运行。我也同意你不应该为此编辑线程池最小值。我只是在解释发生了什么。当您达到最小值时,它会立即停止创建线程。
  • 感谢您的详细回复。我没有意识到 BackgroundWorker 没有使用本机线程。但是,代码在 C# .NET 中仍然可以正常工作;也就是说,它很快。在 Mono 上,它超出了“慢一点”的范围——它慢得令人无法接受,至少慢了 10 倍。在这种情况下,我将探索 BackgroundWorker 的替代方案,但由于上述原因,我怀疑代码是否是确切的问题。
  • 另外,我不相信启动 BackgroundWorkers 真的需要很长时间。当我说“长”时,我的意思是几秒钟,这就是我所看到的。这不是我在 M$ 实现中看到的行为。
猜你喜欢
  • 1970-01-01
  • 2011-07-18
  • 1970-01-01
  • 2020-05-30
  • 1970-01-01
  • 2010-12-03
  • 1970-01-01
  • 2016-06-20
  • 1970-01-01
相关资源
最近更新 更多