Prozesse mit Thread aus Pool starten

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 20 Antworten in diesem Thema. Der letzte Beitrag () ist von Daniel Baumert.

    Prozesse mit Thread aus Pool starten

    Hallo.

    Ich möchte mehrere Prozesse starten und deren Standardoutput auswerten.
    Dies tue ich folgendermaßen:


    VB.NET-Quellcode

    1. Private Sub DoStart(StartPort As Integer, Ports As Integer, Timeout As Integer)
    2. Dim Watch As New Stopwatch
    3. Watch.Start()
    4. Dim AllInstances As New List(Of Thread)
    5. If Ports = 1 Then
    6. W(String.Concat(">> Starting ", CStr(Ports), ConsoleColor.Blue)
    7. Else
    8. W(String.Concat(">> Starting ", CStr(Ports), True, ConsoleColor.Blue)
    9. End If
    10. For i As Integer = StartPort To StartPort + Ports - 1
    11. Dim CurrentPort As Integer = i
    12. Dim T As New Thread(Sub() Prozessstarten(CurrentPort, Timeout))
    13. T.Name = "T" & CStr(CurrentPort)
    14. T.IsBackground = True
    15. T.Start()
    16. AllInstances.Add(T)
    17. Next
    18. Dim Wa As New Thread(Sub()
    19. While True
    20. If Not AllInstances.Any(Function(x) x.IsAlive()) Then
    21. Watch.Stop()
    22. W(String.Concat(Environment.NewLine, Environment.NewLine, " ", Ports, " instance(s) started in ", Watch.Elapsed.Seconds / 2, Watch.Elapsed.Milliseconds, " seconds."), False, ConsoleColor.DarkMagenta)
    23. Exit While
    24. End If
    25. End While
    26. End Sub)
    27. Wa.IsBackground = True
    28. Wa.Start()
    29. End Sub
    30. Private Sub Prozessstarten(Port As Integer, Timeout As Integer)
    31. Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyProgram"))
    32. Dim Pat As String = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MyDict")
    33. Directory.CreateDirectory(Pat)
    34. Dim PSI As ProcessStartInfo = New ProcessStartInfo()
    35. PSI.FileName = "test.exe"
    36. PSI.Arguments = ....
    37. PSI.RedirectStandardOutput = True
    38. PSI.UseShellExecute = False
    39. PSI.WindowStyle = ProcessWindowStyle.Normal
    40. Dim Pro As Process = Process.Start(PSI)
    41. Dim Noww As DateTime = DateTime.Now
    42. While True
    43. Dim T As String = Pro.StandardOutput.ReadLine
    44. If DateTime.Now.Subtract(Noww).Milliseconds >= Timeout Then
    45. W(String.Concat(">> Error: Instance has a timeout."), False, ConsoleColor.Red)
    46. Exit While
    47. End If
    48. If T.Contains("DONE") Then
    49. W(">> started "), False, ConsoleColor.DarkCyan)
    50. Exit While
    51. ElseIf T.Contains("Could not start") Then
    52. W(">> Error: Could not start the instance.", False, ConsoleColor.Red)
    53. Exit While
    54. End If
    55. End While
    56. End Sub



    Bietet sich da eine Möglichkeit, dies mit Threads aus dem Threadpool zu realisieren?
    Achso: Timeouterkennung funktioniert übrigens auch nicht.

    Wenn ja, wie?
    Dafür ist

    ObjectBrowser schrieb:

    System.Diagnostics.Process.BeginOutputReadLine
    und
    System.Diagnostics.Process.OutputDataReceived
    vorgesehen - guck dir die Samples dazu inne MSDN an.

    Besondere Übungen mittm Threadpool sind glaub nicht nötig - das vorgesehene Instrumentarium wird auch in dieser Hinsicht schon leidlich durchdacht sein.

    edit: Hab jetzt das msdn-Beispiel selbst probiert - es ist zum Davonlaufen.

    Dennoch kannst du mit dem Instrumentarium problemlos mehrere Prozesse starten, und von jedem Prozess den Standard-Output (ich glaub sogar zeilenweise) per Event abonnieren - ohne ühaupt mit Threading in Berührung zu kommen.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Habe nun umgeschrieben, aber stehe vor einem Problem:

    Jeder einzelne Prozess, den ich starte, gibt ein Standardoutput zurück.
    Z.b:

    "Started"
    "Could Not Start"
    "60% loaded"

    Diese Werte ich ja mittels Event-Abfrage aus in einem Sub.

    Das Problem ist: wie kann ich jetzt unterscheiden, welcher Prozess es ist?

    Jeder Prozess wird mit anderen Daten gestartet, die ich natürlich auch gerne ausgeben würde , falls gestartet,
    also z.B:

    "Instanz mit Daten xx auf Port xx gestartet".

    Hat jemand eine Idee?



    VB.NET-Quellcode

    1. Private lock As New Object
    2. Private Sub W(T As [String], NewLine As Boolean, Optional C As ConsoleColor = ConsoleColor.Gray)
    3. SyncLock lock
    4. Dim Basiccolor = Console.ForegroundColor
    5. Console.ForegroundColor = C
    6. Console.WriteLine(T)
    7. Console.ForegroundColor = Basiccolor
    8. If NewLine Then Console.WriteLine(Environment.NewLine)
    9. End SyncLock
    10. End Sub
    11. Private Sub Eval(sendingProcess As Object, outLine As DataReceivedEventArgs)
    12. Dim Port As Integer = 0 'Hier müsste man irgendwie an den Port und die anderen Daten gelangen
    13. If Not String.IsNullOrEmpty(outLine.Data) Then SortOutPut.Append(outLine.Data)
    14. Dim T As String = outLine.Data
    15. Dim C As DateTime = StartedList.SingleOrDefault(Function(d) d.Item1 = Port).Item2
    16. If DateTime.Now.Subtract(C).Seconds >= 25 Then W(String.Concat("[Error] Instance 127.0.0.1:", Port, " has a timeout."), False, ConsoleColor.Red)
    17. If T.Contains("Started") Then
    18. W(String.Concat("[Success] Instance 127.0.0.1:", CStr(Port), "!"), False, ConsoleColor.DarkCyan)
    19. ElseIf T.Contains("Could not start") Then
    20. W("[Error]: Could not start instance. Is the port already in use? (Firewall?)", False, ConsoleColor.Red)
    21. End If
    22. End Sub
    23. Private SortOutPut As StringBuilder = New StringBuilder()
    24. Private StartedList As New List(Of Tuple(Of Integer, DateTime))
    25. Private Sub StartNewInstance(Port As Integer, Timeout As Integer)
    26. Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Test"))
    27. Dim Pat As String = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Test", "SocksPort " & CStr(Port))
    28. Directory.CreateDirectory(Pat)
    29. Dim Proc As Process = New Process()
    30. Proc.StartInfo.FileName = "Test.exe"
    31. Proc.StartInfo.Arguments = ....
    32. Proc.StartInfo.RedirectStandardOutput = True
    33. Proc.StartInfo.RedirectStandardInput = True
    34. Proc.StartInfo.UseShellExecute = False
    35. Proc.StartInfo.WindowStyle = ProcessWindowStyle.Normal
    36. AddHandler Proc.OutputDataReceived, AddressOf Eval
    37. Proc.Start()
    38. Proc.BeginOutputReadLine()
    39. Dim T As Tuple(Of Integer, DateTime) = Tuple.Create(Port, DateTime.Now)
    40. If Not StartedList.Exists(Function(c) c.Item1 = Port) Then StartedList.Add(T)
    41. End Sub

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „Daniel Baumert“ ()

    @ErfinderDesRades Jou.
    @Daniel Baumert Du musst die Prozesse als List(Of Process) in der Klasse halten, da hast Du sie permanent verfügbar.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hm. Wie genau soll ich es machen?

    VB.NET-Quellcode

    1. Dim dd As Process = DirectCast(sendingProcess, Process)
    2. Console.WriteLine(dd.StartInfo.Arguments)


    So hätte ich die Startparameter. Eine andere, bessere Möglichkeit gibt es wohl nicht oder?

    List(of Process) wäre denke ich unnötig, weil ich brauche ja für die "Statusanzeige" eigentlich nur die Daten vom
    Prozess, der aktuell, gerade eine Ausgabe hat in der Konsole.

    Daniel Baumert schrieb:

    nur die Daten vom Prozess
    Ja, reicht, das StartInfo-Member bekommst Du da mit, mit allen Einzel-Informationen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Daniel Baumert schrieb:

    die Outputs der einzelnen Instanzen
    Meinst Du eine List(Of T) oder nur eine Liste, wo Du alle AddHandlers aufgeschrieben hast?
    Wenn der betreffende Prozess beendet ist, ist auch der entsprechende Handler inaktiv.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich starte die Prozesse und warte bei jedem einzelnen Prozess auf die
    Konsolenausgabe "100%".

    Einige Prozesse können jedoch auch nicht richtig gestartet werden teilweise -> die werden als failed gezählt.


    Wenn diese Abfrage zutrifft:
    Prozesse gestartet + Prozesse gefailed = Anzahl der zu Startenden Prozesse

    dann brauch kein einziger Standardoutput noch ausgelesen werden. Gekillt werden darf er aber nicht,
    muss weiterlaufen.

    Dafür braucht man doch eine List(Of Process) die man dann durchläuft und auf jeden
    Prozess RemoveHandler ausführt , oder sollte ist Process.CancelOutPutRead auf jeden Process der Liste ausführen?
    Aha.
    Problem: Zu welchem Prozess gehört die aktuelle "100%"?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!