4

我对 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 人同时运行,我觉得两秒钟应该足以让这些工作人员完成。

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

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

4

1 回答 1

9

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

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

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

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

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

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

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

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

编辑:

我决定看看 Mono 的实际性能如何,而不是仅仅依赖于我在 Microsoft 实现中发现的内容。在发现Mono实际上开始了所有6个后台工作之后;他们都在 2000 毫秒的限制内完成!为了记录,这里是结果的比较。我稍微修改了代码以显示发生了什么,但我向您保证,当我按原样运行您的代码时,我得到了相同的结果。“完成创建...”消息在主线程进入 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。没有不同。确保您使用没有附加任何调试器的可执行文件的发布版本对此进行测试。还要确保您在相同的硬件上对其进行测试。有可能,但不太可能,这是您的 linux/mono 安装的问题(假设您在 linux 上运行 mono,而不是 windows)。就像我之前说的,我认为这些结果支持它,我认为这不是问题所在。我会在您的代码中的某个地方寻找其他地方。

于 2012-05-04T02:05:46.380 回答