.Net和Mono中的C#Task.WaitAll()

.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#编译器进行编译,我都可以得到相同的结果。

+0

但是你说'Task.WaitAll'正确超时甚至在单(你在2秒内得到输出,这意味着任务.WaitAll在2秒内返回)。 – Evk

+0

是的,我的坏。抱歉。该计划似乎在等待。我将编辑该问题。 –

+0

如果你删除了'Task.WaitAll'并且在里面用'Dns.GetHostEntry'开始新任务呢?这也会阻止程序完成吗? – Evk

我会回答我的问题,尽管信贷应该去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_SAFEMONO_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(); 
}