我对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");
}
}
}
当我运行上述内容时,我得到以下输出。
Start Start Done Start Start Done Start Done Really Done
首先,并非所有BackgroundWorker
都在两秒内完成。从循环中可以清楚地看出我正在实例化6名工人,但只有3名人员打印“完成”。如果所有6个人同时运行,我觉得两秒钟应该足够让这些工人完成。
真正的内幕故事是我有一个运行良好的应用程序(使用大量的BackgroundWorker
s在后台通过HTTP获取图像。但不同的是,这个应用程序是用C#实现的.NET。当我将应用程序移植到Mono时,BackgroundWorker
似乎只是一次运行一对,并且HTTP连接很容易因此超时。我想知道这是否是案例,因此上面的例子和问题......
这里发生了什么?为什么并非所有BackgroundWorker
同时运行?我需要设置一些参数吗?
答案 0 :(得分:9)
我对Mono实现并不是100%肯定,但我对微软版本的了解相当不错,我只能假设Mono的行为方式相同或类似。
首先,澄清一下,BackgroundWorker
确实,实际上,它产生了.NET线程(与本机线程不同),它与你自己调用ThreadPool.QueueUserWorkItem
相提并论。这是一个微小的细节,可以帮助您理解为什么您会看到这种行为。重要的是要记住.NET中的线程可能实际上并不代表本机线程。实际上,所有6个后台工作程序在运行6个.NET线程时都可以在1-2个本机线程上运行。在我的机器上,实际上只有3个原生线程产生了上述代码。
其次,您没有考虑构建新BackgroundWorker
所需的时间,设置委托,然后异步运行工作程序,这可能涉及也可能不涉及创建本机线程,这实际上是相当昂贵的操作。添加一个计时器,在我的测试中,总共需要一点点的时间来创建所有六个,有时候更长一点。由于调度,ThreadPool可能需要几毫秒才能发现所有线程都饱和/阻塞(因为Thread.Sleep
),然后创建一个新线程。这意味着,在某些情况下,您的后台工作人员可能会在0:01:700左右开始,只剩下300毫秒,但该功能会休眠500毫秒。没有足够的时间来完成任务。
我认为Mono的实现运行速度比微软慢一点是不争的,我确信你可能没有发现你可以在运行时发现同样的行为。
如果有疑问,请责怪自己的代码,因为这通常是问题所在。睡觉你期望完成一项操作的时间,并不是测试某些东西的好方法。如果计算机上运行其他程序会发生什么?如果其他进程的线程抢占了你的,Thread.Sleep
运行速度会慢。在这种情况下,情况会更糟。
如果您要在foo
方法中将代码更改为睡眠5毫秒,并在创建后台工作符后在主方法中等待20毫秒,那么您等待的比率相同,我能够看到以下内容:
Start Start Done Done Start Start Really done Press any key to continue . . .
只有两个完成,甚至没有全部启动。我希望你现在看到测试中的缺陷。它们同时运行,我猜你的问题可能在其他地方。你说你在后台通过http获取图像。请记住,如果某些东西不能正常工作,我会说只有1%的时间实际上有一些时髦的运行时间,另外99%的时间是你的代码。您使用的是同步还是异步I / O?只有一个线程处理请求?我会从那里开始。
编辑:
我决定看看Mono中的性能是什么样的,而不仅仅依赖于我在Microsoft实现中发现的内容。在发现Mono实际上正在开始所有6个背景作品之后;它们都在2000ms的范围内完成!为了记录,这里是结果的比较。我稍微修改了代码以显示正在发生的事情,但我向你保证,当我按原样运行你的代码时,我得到了相同的结果。在主线程进入2000ms睡眠之前打印“完成创建...”消息,其中打印的时间戳是后台工作人员输入其工作方法的时刻。
Windows Mono Done Creating... Done Creating... 00:00:00.0018610 00:00:00.0088775 00:00:00.0019544 Start Start 00:00:00.5074494 Start Start Done Done Done 00:00:00.5615083 00:00:00.5027995 Start Start Done 00:00:00.5028008 00:00:01.0082133 Start Start Done 00:00:01.0082900 Done Start 00:00:01.0025428 Done Start 00:00:01.0618140 00:00:01.0026164 Start Start Done Done Done Done Done Really done Really done
我在完全相同的两个盒子上运行测试(Dell T3400,相同的硬件),一个是Windows 7 x64,另一个是Ubuntu 12.04 x64。没有不同。确保使用发布版本的可执行文件进行测试,而不使用附加的任何调试程序。还要确保您在相同的硬件上进行测试。你的linux / mono安装问题是可能的,但不太可能,这是一个问题(假设你在linux上运行mono,而不是windows)。就像我之前说过的那样,我认为这些结果支持它,我不认为这是问题所在。我会在你的代码的某个地方寻找其他地方。