.Net和Mono中的C#Task.WaitAll()
为什么此代码在Windows和Linux上(使用Mono)的行为不同?.Net和Mono中的C#Task.WaitAll()
static void Main(string[] args)
{
Stopwatch stopwatch = Stopwatch.StartNew();
Task[] tasks = new Task[1];
tasks[0] = Task.Run(() =>
{
IPHostEntry iphe = Dns.GetHostEntry("8.8.8.8.dnsrbl.org");
});
Task.WaitAll(tasks, 2000);
Console.WriteLine("Done in " + stopwatch.ElapsedMilliseconds + " ms");
}
8.8.8.8.dnsrbl.ru
是一个最终会超时的查询的例子。我相信没有工作的DNS服务器(或者它的防火墙阻止了我)。
无论如何,重点不在于从DNS服务器获得结果,关键是Task.WaitAll()
在等待包含调用Dns.GetHostEntry()
的任务时如何在Windows和Mono上运行。
在Windows上,如果查询在超时期限(2s)内没有返回任何结果,程序将运行或多或少的2秒。也就是说,带有超时的Task.WaitAll似乎工作。使用Mono在Linux上运行此程序需要2秒钟才能获得输出,但程序不会终止,直到任务退出。这是为什么?
似乎我得到相同的执行时间,无论我是否使用Time.WaitAll超时或不。
线索在Dns.GetHostEntry()
,因为Task.WaitAll()
按预期工作,如果我使用Thread.Sleep()
模拟长时间运行的任务来启动任务。 可正常工作:
tasks[0] = Task.Run(() => Thread.Sleep(10000));
是否有办法在单声道运行时强制
Task.WaitAll(Task[] tasks, int millisecondsTimeout)
实际超时?
编辑:Task.WaitAll()确实在超时时间后返回,但程序在单声道中运行时不会终止(直到Dns.GetHostEntry
超时)。
它不是编译器。无论使用Visual Studio还是Mono C#编译器进行编译,我都可以得到相同的结果。
我会回答我的问题,尽管信贷应该去EVK谁指导我在正确的轨道上(感谢队友!)
在这个问题上的主题是坏的,至少可以说。这个问题与Task.WaitAll
无关,而是Dns.GetHostEntry
的Mono实现。作为EVK在评论说:
这意味着(最有可能)在Linux上
Dns.GetHostEntry
开始新 非后台线程。直到所有 非后台线程完成,程序才能完成。
的GetHostEntry()
方法位于源文件Dns.cs中,并且当与一个字符串称为它调用GetHostByName
然后调用GetHostByName_internal
这是位于w32socket.c外部的C函数。最后mono_get_address_info
(在networking-posix.c中)被调用,我们在libc函数getaddrinfo
中关闭。唷!
我看不到正在启动任何新的非后台线程,但我发现这一点:
MONO_ENTER_GC_SAFE;
ret = getaddrinfo (hostname, service_name, &hints, &info);
MONO_EXIT_GC_SAFE;
MONO_ENTER_GC_SAFE
和MONO_EXIT_GC_SAFE
是单线程的API定义的宏。h
#define MONO_ENTER_GC_SAFE \
do { \
gpointer __gc_safe_dummy; \
gpointer __gc_safe_cookie = mono_threads_enter_gc_safe_region (&__gc_safe_dummy)
#define MONO_EXIT_GC_SAFE \
mono_threads_exit_gc_safe_region (__gc_safe_cookie, &__gc_safe_dummy); \
} while (0)
我没有进一步挖掘,但我相信Evk是正确的。
因此,我的问题的答案:Dns.GetHostEntry()
无法在Mono中终止或取消。调用此方法的程序在所有查询处理完毕或超时之前都不会终止。它就是这样儿的。我的猜测是这与垃圾收集器(GC)有关,它可能在非后台线程中运行,因此无法取消/终止。
编辑:(几天后)一旦我找到getaddrinfo
的手册页,原因很明显。该函数返回结果的链表。这个列表当然分配在堆上,并且必须在某个时候释放以避免内存泄漏。这个功能是freeaddrinfo
。
无论如何,再次感谢Evk!
那么,我们如何能够与超时(使用单声道)并行地触发多个DNS查询?简单!该功能可以在任务中被调用,它会很乐意听从了WaitAll与超时:
private static string host(string query)
{
ProcessStartInfo psi = new ProcessStartInfo("host", query);
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
Process p = Process.Start(psi);
return p.StandardOutput.ReadToEnd();
}
但是你说'Task.WaitAll'正确超时甚至在单(你在2秒内得到输出,这意味着任务.WaitAll在2秒内返回)。 – Evk
是的,我的坏。抱歉。该计划似乎在等待。我将编辑该问题。 –
如果你删除了'Task.WaitAll'并且在里面用'Dns.GetHostEntry'开始新任务呢?这也会阻止程序完成吗? – Evk