.Net 和 Mono 中的 C# Task.WaitAll()
C# Task.WaitAll() in .Net and Mono
为什么此代码在 Windows 和 Linux(使用 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 服务器(或者它的防火墙阻止了我)。
无论如何,重点不是从 DNS 服务器获得结果,重点是 Task.WaitAll()
在 Windows 和 Mono 上的行为如何等待包含对 [=15= 的调用的任务].
在 Windows 上,当查询未在超时期限(2 秒)内 return 获得任何结果时,程序需要或多或少 2 秒才能 运行 。也就是说, Task.WaitAll with timeout 似乎有效。 运行 这个在 Linux 上使用 Mono 的程序需要 2 秒才能获得输出,但程序在任务退出之前不会终止。这是为什么?
无论是否使用 Time.WaitAll 超时,我似乎都获得相同的执行时间。
线索在 Dns.GetHostEntry()
中,因为如果我用 Thread.Sleep()
模拟长 运行ning 任务开始任务,Task.WaitAll()
会按预期工作。
这按预期工作:
tasks[0] = Task.Run(() => Thread.Sleep(10000));
有没有办法强制 Task.WaitAll(Task[] tasks, int millisecondsTimeout)
在 Mono 中 运行 时实际超时?
编辑:Task.WaitAll() 实际上在超时期限后执行 return,但当 运行 在 Mono 中运行时程序不会终止(直到 Dns.GetHostEntry
超时)。
而且它不是编译器。无论我使用 Visual Studio 还是使用 Mono C# 编译器进行编译,我都会得到相同的结果。
我会回答我自己的问题,尽管应该归功于引导我走上正确轨道的 Evk(感谢伙伴!)
这个问题的题目至少可以说是糟糕的。该问题与 Task.WaitAll
无关,而与 Dns.GetHostEntry
的 Mono 实现有关。正如 Evk 在评论中所说:
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
,这是一个外部 C 函数位于 w32socket.c。最后 mono_get_address_info
(在网络中-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) 有关,它可能在非后台线程中运行,因此不能 cancelled/terminated.
编辑:(几天后)一旦我找到 getaddrinfo
的手册页,其原因就很明显了。这个函数 returns 一个指向结果的链表。这个列表当然是在堆上分配的,并且必须在某个时候释放以避免内存泄漏。该函数是 freeaddrinfo
。
无论如何,再次感谢 Evk!
那么我们如何在超时的情况下并行触发多个 DNS 查询(使用 Mono)?简单!这个函数可以在任务中调用,它会很乐意服从 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();
}
为什么此代码在 Windows 和 Linux(使用 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 服务器(或者它的防火墙阻止了我)。
无论如何,重点不是从 DNS 服务器获得结果,重点是 Task.WaitAll()
在 Windows 和 Mono 上的行为如何等待包含对 [=15= 的调用的任务].
在 Windows 上,当查询未在超时期限(2 秒)内 return 获得任何结果时,程序需要或多或少 2 秒才能 运行 。也就是说, Task.WaitAll with timeout 似乎有效。 运行 这个在 Linux 上使用 Mono 的程序需要 2 秒才能获得输出,但程序在任务退出之前不会终止。这是为什么?
无论是否使用 Time.WaitAll 超时,我似乎都获得相同的执行时间。
线索在 Dns.GetHostEntry()
中,因为如果我用 Thread.Sleep()
模拟长 运行ning 任务开始任务,Task.WaitAll()
会按预期工作。
这按预期工作:
tasks[0] = Task.Run(() => Thread.Sleep(10000));
有没有办法强制 Task.WaitAll(Task[] tasks, int millisecondsTimeout)
在 Mono 中 运行 时实际超时?
编辑:Task.WaitAll() 实际上在超时期限后执行 return,但当 运行 在 Mono 中运行时程序不会终止(直到 Dns.GetHostEntry
超时)。
而且它不是编译器。无论我使用 Visual Studio 还是使用 Mono C# 编译器进行编译,我都会得到相同的结果。
我会回答我自己的问题,尽管应该归功于引导我走上正确轨道的 Evk(感谢伙伴!)
这个问题的题目至少可以说是糟糕的。该问题与 Task.WaitAll
无关,而与 Dns.GetHostEntry
的 Mono 实现有关。正如 Evk 在评论中所说:
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
,这是一个外部 C 函数位于 w32socket.c。最后 mono_get_address_info
(在网络中-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) 有关,它可能在非后台线程中运行,因此不能 cancelled/terminated.
编辑:(几天后)一旦我找到 getaddrinfo
的手册页,其原因就很明显了。这个函数 returns 一个指向结果的链表。这个列表当然是在堆上分配的,并且必须在某个时候释放以避免内存泄漏。该函数是 freeaddrinfo
。
无论如何,再次感谢 Evk!
那么我们如何在超时的情况下并行触发多个 DNS 查询(使用 Mono)?简单!这个函数可以在任务中调用,它会很乐意服从 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();
}