Vorwort: Ich bin mir bewusst, dass die Verwendung des ThreadPool (entweder über TPL
oder direkt) für IO-Operationen is generally frowned upon weil IO unbedingt sequentiell ist, bezieht sich mein Problem auf "parallel IO" mit blockierenden Aufrufen Setzen Sie keine Async
Methode ein.Verwenden von TPL mit parallel blockierenden IO-Operationen
Ich bin ein GUI-Tool zu schreiben, die Informationen über Computer im Netzwerk abruft, das dies tut (vereinfachte Code):
String[] computerNames = { "foo", "bar", "baz" };
foreach(String computerName in computerNames) {
Task.Factory
.StartNew(GetComputerInfo, computerName)
.ContinueWith(ShowOutputInGui, RunOnGuiThread);
}
private ComputerInfo GetComputerInfo(String machineName) {
Task<Int64> pingTime = Task.Factory.StartNew(() => GetPingTime(machineName));
Task<Process[]> processes = Task.Factory.StartNew(() => System.Diagnostics.Process.GetProcesses(machineName));
// and loads more
Task.WaitAll(pingtime, processes, etc);
return new ComputerInfo(pingTime.Result, processes.Result, etc);
}
Wenn ich diesen Code ausführen Ich finde es eine überraschend lange dauert Menge Zeit zu laufen, verglichen mit dem alten sequentiellen Code, den ich hatte.
beachten, dass jede Aufgabe in der GetComputerInfo
Verfahren ist völlig unabhängig von anderen um ihn herum (zB Ping Zeit getrennt von GetProcesses
berechnet werden), doch, wenn ich einige Stopwatch
Timing Anrufe eingefügt entdeckte ich, dass die einzelnen Teilaufgaben, wie der GetProcesses
Anruf wurde nur bis zu 3000ms
gestartet, nachdem GetComputerInfo
aufgerufen worden war - es gibt einige große Verzögerung, die weitergehen.
Ich bemerkte, dass, wenn ich die Anzahl der äußeren parallelen Aufrufe in GetComputerInfo
reduzieren (durch die Größe des Arrays computerNames
zu reduzieren) die ersten Ergebnisse fast sofort zurückgegeben wurden. Einige der Computernamen sind für Computer, die ausgeschaltet sind, so genannte GetProcesses
und PingTime
nehmen eine sehr lange Zeit vor dem Zeitlimit (mein echter Code fängt die Ausnahmen). Dies liegt wahrscheinlich daran, dass die Offline-Computer Tasks
blockieren, die ausgeführt werden, und die TPL beschränkt sie natürlich auf die Anzahl der CPU-Hardware-Threads (8).
Gibt es eine Möglichkeit, TPL mitzuteilen, dass die inneren Aufgaben (z. B. GetProcesses
) keine äußeren Aufgaben blockieren (GetComputerInfo
)?
(Ich habe versucht, die "Eltern/Kind" Aufgabe Anlage/Blockierung, aber es gilt nicht für meine Situation, wie ich nie untergeordnete Aufgaben nie explizit an übergeordneten Aufgaben anfügen, und die übergeordnete Aufgabe natürlich mit Task.WaitAll
wartet sowieso) .
Wahrscheinlich wäre besser, wenn 'GetComputerInfo () '' '' '' '' '_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ Warum nicht 'Task []' zurückgeben? – MickyD
Ohne eine gute [mcve], die das Problem zuverlässig reproduziert, ist es schwierig, wenn nicht gar unmöglich, das Problem zu diagnostizieren. Beachten Sie jedoch, dass der Thread-Pool nicht unbegrenzt viele Threads bereithält. Im Leerlauf hat es nur eine Handvoll Threads max (gleich der Anzahl der CPU-Kerne) und startet nur alle halbe Sekunde bis Sekunde neue Threads (IIRC ist konfigurierbar und ich kann mich nicht an den Standard erinnern). Sie können Dinge helfen, indem Sie a) die async ping-Methode verwenden und b) 'ThreadPool.SetMinThreads()' verwenden, um die Anzahl der Threads im Leerlauf zu erhöhen, damit sie fertig sind, wenn Sie sie wollen. –
Beachten Sie, dass der Aufruf von 'SetMinThreads()' ein bisschen wie ein Hack ist. Es ist bedauerlich, dass .NET keine asynchrone Version der 'GetProcesses()' Methode hat; aber ich sehe nicht einmal eine asynchrone Methode, dies in nativem Code zu tun. Wenn Sie wirklich wollen, dass die Operationen parallel sind, stecken Sie fest, die Threads selbst zu verwalten, denke ich. –