.NET和Mono中的C#Task.WaitAll() [英] C# Task.WaitAll() in .Net and Mono

查看:97
本文介绍了.NET和Mono中的C#Task.WaitAll()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么此代码在Windows和Linux(使用Mono)上的行为不同?

Why does this code behave different on Windows and Linux (using Mono)?

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服务器(或其防火墙阻止了我).

8.8.8.8.dnsrbl.ru is an example of a query that will eventually time out. I believe there is no working DNS server (or its firewall is blocking me).

无论如何,关键不是从DNS服务器获得结果,关键是Task.WaitAll()在等待包含对Dns.GetHostEntry()的调用的任务时在Windows和Mono上的行为.

Anyway, the point is not getting a result from the DNS server, the point is how Task.WaitAll() behaves on Windows and Mono when waiting for a task containing a call to Dns.GetHostEntry().

在Windows上,如果查询在超时时间段(2s)内未返回任何结果,则该程序大约需要2秒钟才能运行.也就是说,带有超时的Task.WaitAll似乎可以正常工作.使用Mono在Linux上运行该程序需要2秒钟才能获得输出,但是该程序直到任务退出后才会终止.为什么会这样?

On Windows the program takes more or less 2 seconds to run when the query is not returning any result within the timeout period (2s). That is, Task.WaitAll with timeout seems to work. Running this program on Linux with Mono takes 2 seconds to get the output, but the program does not terminate until the task exits. Why is that?

无论我是否将Time.WaitAll与超时一起使用,似乎我都获得了相同的执行时间.

It seems like I get the same execution time regardless if I use Time.WaitAll with timeout or not.

线索出在Dns.GetHostEntry()中,因为如果我使用Thread.Sleep()模拟长期运行的任务来启动任务,则Task.WaitAll()会按预期工作. 可以按预期工作:

The clue is in Dns.GetHostEntry() because Task.WaitAll() works as expected if I start a task with a Thread.Sleep() simulating a long running task. This works as expected:

tasks[0] = Task.Run(() => Thread.Sleep(10000));

在Mono中运行时,是否有办法强制Task.WaitAll(Task[] tasks, int millisecondsTimeout)实际超时?

Is there a way to force Task.WaitAll(Task[] tasks, int millisecondsTimeout) to actually time out when running in Mono?

Task.WaitAll()实际上确实在超时时间后返回,但是在Mono中运行时程序不会终止(直到Dns.GetHostEntry超时).

Task.WaitAll() does in fact return after the timeout period, but the program doesn't terminate when running in Mono (until Dns.GetHostEntry times out).

它不是编译器.无论我使用Visual Studio还是Mono C#编译器进行编译,我都得到相同的结果.

And it's not the compiler. I get the same result whether I compile with Visual Studio or the Mono C# compiler.

推荐答案

我将回答我自己的问题,尽管应该归功于Evk,后者在正确的轨道上指导了我(感谢队友!)

I’ll answer my own question although credit should go to Evk who guided me on the right track (thanks mate!)

这个问题的主题至少可以说是不好的.该问题与Task.WaitAll无关,而与Dns.GetHostEntry的Mono实现无关.正如Evk在评论中所说:

The subject on this question is bad to say the least. The problem has nothing to do with Task.WaitAll but rather the Mono implementation of Dns.GetHostEntry. As Evk said in a comment:

这(很可能)意味着linux上的Dns.GetHostEntry开始新的 非后台线程.程序无法完成,直到全部 非后台线程已完成.

That means (most likely) that Dns.GetHostEntry on linux starts new non-background thread. Program cannot complete until all non-background threads are finished.

GetHostEntry()方法位于源文件Dns.cs中,当使用字符串调用时,它会调用GetHostByName,然后调用GetHostByName_internal,这是位于w32socket.c中的外部C函数.最后,调用mono_get_address_info(在networking-posix.c中),并且我们进入了libc函数getaddrinfo. !

The GetHostEntry() method is located in the source file Dns.cs and when called with a string it calls GetHostByName which then calls GetHostByName_internal which is an external C function located in w32socket.c. Finally mono_get_address_info (in networking-posix.c) is called and we are down in the libc function getaddrinfo. Phew!

我看不到任何新的非后台线程正在启​​动,但是我发现了这一点:

I cannot see any new non-background threads being started, but I found this:

MONO_ENTER_GC_SAFE;
ret = getaddrinfo (hostname, service_name, &hints, &info);
MONO_EXIT_GC_SAFE;

MONO_ENTER_GC_SAFEMONO_EXIT_GC_SAFE是在mono-threads-api.h中定义的宏

MONO_ENTER_GC_SAFE and MONO_EXIT_GC_SAFE are macros defined in mono-threads-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是正确的.

I did not dig any further, but I believe Evk is right.

因此,我的问题的答案:Dns.GetHostEntry()在Mono中无法终止或取消.在所有查询都已处理或超时之前,调用此方法的程序不会终止.它就是这样儿的.我的猜测是,垃圾收集器(GC)可能在非后台线程中运行,因此无法取消/终止.

So, the answer to my question: Dns.GetHostEntry() cannot be terminated or cancelled in Mono. A program calling this method will not terminate until all queries has been processed or timed out. That’s the way it is. My guess is that is has to do with the garbage collector (GC) which probably runs in non-background thread and hence cannot be cancelled/terminated.

(几天后)一旦我进入getaddrinfo的手册页,其原因就显而易见了.此函数将链接列表返回到结果.该列表当然是在堆上分配的,并且必须在某个时候释放以避免内存泄漏.该功能是freeaddrinfo.

(A few days later) The reason for this was obvious once I got down to the man-page for getaddrinfo. This function returns a linked list to the result. This list is of course allocated on the heap, and has to be freed at some point to avoid memory leakage. The function for that is freeaddrinfo.

无论如何,再次感谢Evk!

Anyway, thanks again to Evk!

那么我们如何在超时的情况下并行触发多个DNS查询(使用Mono)?简单!可以在Task中调用此函数,并且很乐意在超时的情况下服从WaitAll:

So how can we fire multiple DNS queries in parallel with a timeout (using Mono)? Easy! This function can be called in a Task and it will be happy to obey WaitAll with timeout:

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();
}

这篇关于.NET和Mono中的C#Task.WaitAll()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆