簡體   English   中英

.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()在等待包含對Dns.GetHostEntry()的調用的任務時在Windows和Mono上的行為。

在Windows上,當查詢未在超時期限(2s)內返回任何結果時,程序運行大約需要2秒鍾。 也就是說,具有超時的Task.WaitAll似乎有效。 使用Mono在Linux上運行此程序需要2秒鍾才能獲得輸出,但程序在任務退出之前不會終止。 這是為什么?

似乎我得到相同的執行時間,無論我是否使用Time.WaitAll超時或不。

線索在Dns.GetHostEntry()因為如果我使用模擬長時間運行任務的Thread.Sleep()啟動任務, Task.WaitAll()按預期工作。 這按預期工作:

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

有沒有辦法強制 Task.WaitAll(Task[] tasks, int millisecondsTimeout)在Mono中運行時實際超時?

編輯:Task.WaitAll()確實在超時期限后返回,但程序在Mono中運行時不會終止(直到Dns.GetHostEntry超時)。

而且它不是編譯器。 無論是使用Visual Studio還是使用Mono C#編譯器編譯,我都會得到相同的結果。

我會回答我自己的問題,雖然應該歸功於Evk,他引導我走上正軌(感謝隊友!)

這個問題的主題至少可以說是壞事。 這個問題有沒有關系Task.WaitAll而是單執行Dns.GetHostEntry 正如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是在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是對的。

所以,我的問題的答案: Dns.GetHostEntry()無法在Mono中終止或取消。 在處理或超時所有查詢之前,調用此方法的程序不會終止。 它就是這樣兒的。 我的猜測是與垃圾收集器(GC)有關,它可能在非后台線程中運行,因此無法取消/終止。

編輯:(幾天后)一旦我進入getaddrinfo的man-page,這個原因很明顯。 此函數返回結果的鏈接列表。 此列表當然是在堆上分配的,必須在某些時候釋放以避免內存泄漏。 其功能是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();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM