Beschreibung:
Mit diesem Programm können Konsolenprogramme (CMD), oder Befehle parallel auf verschiedenen Servern ausgeführt werden. Minderst Voraussetzung ist eine "server.txt", mit zeilenweise Namen der Server im gleichen Verzeichnis und die Übergabe von dem Dateinamen der Batch, oder eines Befehls, das mehrfach ausgeführt werden soll.
Die Namen der Server werden bei einem Befehl als %%1 und bei Batchdateien als %1 übergeben.
Wenn das Programm läuft, legt es ein "tmp" - Verzeichnis an, in dem die Ausgaben (StdOut, StdErr) in eine Datei (Servernamen) geschrieben werden. Am Ende wird eine Datei erzeugt, mit Datum und Zeit erweitert, in der die Information aus allen Servern zusammen gefasst wird.
Die Hilfe wird wie üblich mit dem Parameter /? oder ohne Angabe von Parametern ausgegeben.
Hier noch die Anzeige der Hilfe:
Startet mehrere Prozesse gleichzeitig mittels Übergabe einer Liste von Server
und der optionalen Vorgabe der maximal gleichzeitig laufenden Prozesse.
Zusätzlich besteht die Möglichkeit vor dem ausführen der Prozesse, die
Erreichbarkeit der Server mittels PING zu ermitteln.
Die Ausgaben (stdOut/stdErr) werden in separaten Protokolldateien erfaßt und
anschließend in einer Protokolldatei zusammengefügt. Fehlermeldungen werden
zusätzlich als Information auf der Konsole ausgegeben.
Die Reihenfolge in der die Server in der Protokolldatei ausgegeben werden,
ist abhängig von der Geschwindigkeit mit der die Server antworten.
Der Name der Protokolldatei hat folgenden Syntax:
FastCMD dd.mm.yyyy hh-mm-ss.txt
Copyright 2014 by Andreas Potsch - Mail an
[email protected]
Syntax: FastCmd [Befehl|Programm] [Server.txt] [pTIMEOUT] [maxAnzahl]
Befehl/Programm Befehl|Programm, der|daß mehrfach ausgeführt werden soll.
server.txt Eine Textdatei, in der die Servername zeilenweise angegeben
sind, die bei einem Befehl als %%1 und bei einem
Batch-Programm als %1 übergeben werden.
Wichtig!!! Servernamen dürfen nur einmal in der server.txt
vorhanden sein!!!
pTIMEOUT Mit diesem Parameters ist es möglich, vor dem ausführen
der Befehle|Programme ein PING auf den entsprechenden
Server auszuführen. Ist der PING erfolglos, dann wird
dies in die Protokolldatei eingetragen und zusätzlich in
der Konsole ausgegeben. Die Zahl nach dem Buchstaben legt
die Wartezeit (timeout) bis zur Antwort fest. Ist keine
Zahl angegeben, wird der Wert auf 1000ms festgelegt.
Der Parameter ist optional.
maxAnzahl Legt die maximale Anzahl der gleichzeitig ausgeführten
Befehle/Programme fest. Wird dieser Parameter nicht
vorgegeben, starten soviel Befehle/Programme gleichzeitig,
wie Server in der Liste vorhanden sind (maximal 999).
ACHTUNG!! Die Anzahl der gleichzeitig gestarteten Programme,
ist abhängig von dem zur Verfügung stehenden Speicher.
Der Parameter ist optional.
Beispiele:
FastCmd dir \\%%1\c$\Daten server.txt
FastCMD batch.cmd server.txt p5000 50
In der batch.cmd wird %1 als Platzhalter für den jeweiligen
Name des Server aus der server.txt übergeben.
Programm.cs
C#-Code: |
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
namespace FastCmd
{
class Program
{
static int prozessRunning = 0;
static int prozessComplete = 0;
static int prozessStarted = 0;
static int prozessesExecuted = 0;
static StringBuilder returnString = null;
static int Main(string[] args)
{
try
{
List<string> server = new List<string>();
bool isFile = false;
bool isPing = false;
int timeout = 1000;
returnString = new StringBuilder("");
int MaxProzessCount = 999;
if (args.Length == 0)
{
Help();
return 0;
}
if (args[0] == "/?")
{
Help();
return 0;
}
if (args.Length < 2)
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Bitte mindestens zwei Parameter angeben!");
Console.WriteLine("");
Help();
return 1;
}
if (!File.Exists(args[1]))
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Serverdatei ist nicht vorhanden!!!");
Console.WriteLine("");
Help();
return 1;
}
if (args.Length > 2)
{
if (args[2].ToLower().ToString().Contains("p") == true)
{
if (args[2].Length > 1)
{
int num;
bool test = int.TryParse(args[2].Substring(1), out num);
if (test == true)
{
if (num > 0)
{
timeout = num;
}
else
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Die Zahl nach 'p' muß größer als null sein!!!");
Console.WriteLine("");
Help();
return 1;
}
}
else
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Nach dem Buchstaben 'p' muß eine Zahl erfolgen!!!");
Console.WriteLine("");
Help();
return 1;
}
}
isPing = true;
}
else
{
int num;
bool test = int.TryParse(args[2], out num);
if (test == false)
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Bitte eine Zahl als dritten Parameter eingeben,");
Console.WriteLine(" oder mit dem Buchstaben 'p' beginnen lassen!!!");
Console.WriteLine("");
Help();
return 1;
}
if (num >= 999)
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Bitte keine Zahl über 999 eingeben!!!");
Console.WriteLine("");
Help();
return 1;
}
MaxProzessCount = num;
}
}
if (args.Length > 3)
{
int num;
bool test = int.TryParse(args[3], out num);
if (test == false)
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Bitte eine Zahl als vierten Parameter eingeben!!!");
Console.WriteLine("");
Help();
return 1;
}
if (num >= 999)
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Bitte keine Zahl über 999 eingeben!!!");
Console.WriteLine("");
Help();
return 1;
}
MaxProzessCount = num;
}
if (File.Exists(args[0]))
isFile = true;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
if (Directory.Exists(Environment.CurrentDirectory + "\\tmp"))
Directory.Delete(Environment.CurrentDirectory + "\\tmp", true);
Directory.CreateDirectory(Environment.CurrentDirectory + "\\tmp");
string line;
StreamReader file = new StreamReader(args[1]);
line = file.ReadLine().ToLower().Trim();
if ((line == null) || (line == ""))
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Keine Server in der Serverdatei vorhanden,");
Console.WriteLine(" oder die erste Zeile ist leer!!!");
Console.WriteLine("");
Help();
return 1;
}
else
server.Add(line);
while ((line = file.ReadLine().ToLower().Trim()) != null)
{
if (line != String.Empty)
{
if (!server.Contains(line))
{
server.Add(line);
}
else
{
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Servername: " + line.ToUpper() + " ist doppelt vorhanden!!!");
Console.WriteLine("");
Help();
return 1;
}
}
}
prozessesExecuted = server.Count;
NewProzess[] prozessList = new NewProzess[server.Count];
for (int i = 0; i < server.Count; i++)
{
prozessList[i] = new NewProzess(args[0], server[i], isFile, isPing, timeout);
prozessList[i].prozessCompleted +=
new NewProzess.ProzessCompletedDelegat(newProzess_completeProzess);
}
if (prozessList.Length < MaxProzessCount)
MaxProzessCount = prozessList.Length;
Thread showActiviti = new Thread(new ThreadStart(StartActiviti));
showActiviti.Start();
while (prozessComplete < server.Count)
{
if (prozessRunning < MaxProzessCount)
{
if (prozessStarted < prozessList.Length)
{
prozessList[prozessStarted].StartProzess();
prozessRunning++;
prozessStarted++;
}
}
}
DateTime time = DateTime.Now;
string format = "dd.MM.yyyy HH-mm-ss";
string writeFileName = string.Format("{0}\\{1}.txt",
Environment.CurrentDirectory,
Process.GetCurrentProcess().ProcessName +
" " + time.ToString(format));
StreamWriter proFile = new StreamWriter(writeFileName);
proFile.AutoFlush = true;
for (int i = 0; i < server.Count; i++)
{
string allLine = "####### " + server[i] +
" ###########################################################################" +
Environment.NewLine;
string readFileName = String.Empty;
readFileName = string.Format("{0}\\tmp\\{1}.txt",
Environment.CurrentDirectory, server[i]);
if (File.Exists(readFileName))
{
StreamReader resfile = new StreamReader(readFileName);
allLine += resfile.ReadToEnd();
proFile.Write(allLine);
resfile.Close();
}
else
{
proFile.Write(allLine);
proFile.WriteLine("");
proFile.WriteLine("!!! Ausgabedatei nicht vorhanden, oder kein Ping möglich !!!");
proFile.WriteLine("");
}
}
proFile.Flush();
Thread.Sleep(50);
proFile.Close();
if(!File.Exists(Environment.CurrentDirectory + "\\tmp"))
Directory.Delete(Environment.CurrentDirectory + "\\tmp", true);
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
string elapsedTime = String.Format("{0:D2}:{1:D2}.{2:D3}",
ts.Minutes, ts.Seconds,
ts.Milliseconds);
if (returnString.ToString() != "")
{
Console.WriteLine("");
Console.WriteLine(returnString);
}
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("Laufzeit: " + elapsedTime);
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
return 0;
}
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine(ex.ToString());
Console.WriteLine("");
return 1;
}
}
static void newProzess_completeProzess(object sender, prozessReturnArgs e)
{
try
{
prozessRunning--;
prozessComplete++;
if (e.server != String.Empty)
{
if (e.ErrMsg != String.Empty)
{
returnString.Append(Environment.NewLine +
"kein Ping möglich -> " + e.server);
}
else
{
returnString.Append(Environment.NewLine +
"Fehler in Logdatei -> " + e.server);
}
}
}
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
static void StartActiviti()
{
try
{
int counter = 0;
int delay = 0;
bool notCompeled = true;
string line = "";
Console.WriteLine("");
string[] activSymbol = new string[] { "|", "/", "-", "\\" };
while (notCompeled)
{
if (prozessComplete != prozessesExecuted)
{
line = string.Format("Prozesse aktiv: {0,3} -> {1,3} Prozesse beendet ({2,3}%) {3} \r",
prozessRunning,
prozessComplete,
prozessComplete * 100 / prozessesExecuted,
activSymbol[counter]);
}
else
{
line = string.Format("Prozesse aktiv: {0,3} -> {1,3} Prozesse beendet ({2,3}%) \r",
prozessRunning,
prozessComplete,
prozessComplete * 100 / prozessesExecuted);
notCompeled = false;
}
Console.Write(line);
Thread.Sleep(5);
if (++delay > 5)
{
delay = 0;
if (++counter > activSymbol.Length - 1)
counter = 0;
}
}
}
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
static void Help()
{
Console.WriteLine("");
Console.WriteLine("Startet mehrere Prozesse gleichzeitig mittels Übergabe einer Liste von Server");
Console.WriteLine("und der optionalen Vorgabe der maximal gleichzeitig laufenden Prozesse.");
Console.WriteLine("");
Console.WriteLine("Zusätzlich besteht die Möglichkeit vor dem ausführen der Prozesse, die");
Console.WriteLine("Erreichbarkeit der Server mittels PING zu ermitteln.");
Console.WriteLine("");
Console.WriteLine("Die Ausgaben (stdOut/stdErr) werden in separaten Protokolldateien erfaßt und");
Console.WriteLine("anschließend in einer Protokolldatei zusammengefügt. Fehlermeldungen werden");
Console.WriteLine("zusätzlich als Information auf der Konsole ausgegeben.");
Console.WriteLine("");
Console.WriteLine("Die Reihenfolge in der die Server in der Protokolldatei ausgegeben werden,");
Console.WriteLine("ist abhängig von der Geschwindigkeit mit der die Server antworten.");
Console.WriteLine("");
Console.WriteLine("Der Name der Protokolldatei hat folgenden Syntax:");
Console.WriteLine("");
Console.WriteLine(" " + Process.GetCurrentProcess().ProcessName +
" dd.mm.yyyy hh-mm-ss.txt");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("Copyright 2014 by Andreas Potsch - Mail an [email protected]");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("Syntax: FastCmd [Befehl|Programm] [Server.txt] [pTIMEOUT] [maxAnzahl]");
Console.WriteLine("");
Console.WriteLine(" Befehl/Programm Befehl|Programm, der|daß mehrfach ausgeführt werden soll.");
Console.WriteLine("");
Console.WriteLine(" server.txt Eine Textdatei, in der die Servername zeilenweise angegeben");
Console.WriteLine(" sind, die bei einem Befehl als %%1 und bei einem");
Console.WriteLine(" Batch-Programm als %1 übergeben werden.");
Console.WriteLine(" Wichtig!!! Servernamen dürfen nur einmal in der server.txt");
Console.WriteLine(" vorhanden sein!!!");
Console.WriteLine("");
Console.WriteLine(" pTIMEOUT Mit diesem Parameters ist es möglich, vor dem ausführen");
Console.WriteLine(" der Befehle|Programme ein PING auf den entsprechenden");
Console.WriteLine(" Server auszuführen. Ist der PING erfolglos, dann wird");
Console.WriteLine(" dies in die Protokolldatei eingetragen und zusätzlich in");
Console.WriteLine(" der Konsole ausgegeben. Die Zahl nach dem Buchstaben legt");
Console.WriteLine(" die Wartezeit (timeout) bis zur Antwort fest. Ist keine");
Console.WriteLine(" Zahl angegeben, wird der Wert auf 1000ms festgelegt.");
Console.WriteLine(" Der Parameter ist optional.");
Console.WriteLine("");
Console.WriteLine(" maxAnzahl Legt die maximale Anzahl der gleichzeitig ausgeführten");
Console.WriteLine(" Befehle/Programme fest. Wird dieser Parameter nicht");
Console.WriteLine(" vorgegeben, starten soviel Befehle/Programme gleichzeitig,");
Console.WriteLine(" wie Server in der Liste vorhanden sind (maximal 999).");
Console.WriteLine(" ACHTUNG!! Die Anzahl der gleichzeitig gestarteten Programme,");
Console.WriteLine(" ist abhängig von dem zur Verfügung stehenden Speicher.");
Console.WriteLine(" Der Parameter ist optional.");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" Beispiele: " + Process.GetCurrentProcess().ProcessName +
" \"dir \\\\%%1\\c$\\Daten\" server.txt");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(" " + Process.GetCurrentProcess().ProcessName +
" batch.cmd server.txt p5000 50");
Console.WriteLine("");
Console.WriteLine(" In der batch.cmd wird %1 als Platzhalter für den jeweiligen");
Console.WriteLine(" Name des Server aus der server.txt übergeben.");
Console.WriteLine("");
Console.WriteLine("");
}
}
}
|
NewProzess
C#-Code: |
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace FastCmd
{
class NewProzess
{
public delegate void ProzessCompletedDelegat(object sender, prozessReturnArgs e);
public event ProzessCompletedDelegat prozessCompleted;
private Process myProzess = new Process();
private string arguments = String.Empty;
private string fileName = String.Empty;
private bool isPing = false;
private int timeout = 0;
private StringBuilder stdOutput = null;
private int numOutputLines = 0;
private string taskError = String.Empty;
private string msgError = String.Empty;
prozessReturnArgs args = new prozessReturnArgs();
public NewProzess(string fileName, string arguments, bool isFile, bool isPing, int timeout)
{
try
{
this.arguments = arguments;
this.fileName = fileName;
this.isPing = isPing;
this.timeout = timeout;
myProzess.OutputDataReceived += new DataReceivedEventHandler(myProzess_OutputDataReceived);
myProzess.ErrorDataReceived += new DataReceivedEventHandler(myProzess_ErrorDataReceived);
if (isFile == true)
{
myProzess.StartInfo.FileName = fileName;
myProzess.StartInfo.Arguments = arguments;
}
else
{
myProzess.StartInfo.FileName = "cmd.exe";
if (arguments.Contains("%1"))
{
fileName = fileName.Replace("%1", arguments);
}
fileName = string.Format(" /c {0}", fileName);
myProzess.StartInfo.Arguments = fileName;
}
myProzess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
myProzess.StartInfo.UseShellExecute = false;
myProzess.StartInfo.RedirectStandardOutput = true;
myProzess.StartInfo.RedirectStandardError = true;
}
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
void myProzess_ErrorDataReceived(object sendingProzess, DataReceivedEventArgs e)
{
try
{
if (!String.IsNullOrEmpty(e.Data))
{
numOutputLines++;
stdOutput.Append(Environment.NewLine + "ERROR: " + e.Data);
taskError = arguments;
}
}
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
void myProzess_OutputDataReceived(object sendingProzess, DataReceivedEventArgs e)
{
try
{
if (!String.IsNullOrEmpty(e.Data))
{
numOutputLines++;
stdOutput.Append(Environment.NewLine + e.Data);
}
}
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
public void StartProzess()
{
try
{
Thread myThread = new Thread(new ThreadStart(StartTask));
myThread.Start();
}
catch (System.ArgumentNullException ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
catch (System.OutOfMemoryException ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
private void StartTask()
{
try
{
stdOutput = new StringBuilder("");
if (isPing == true)
{
PingIP ping = new PingIP(arguments, timeout);
if (ping.result == true)
{
myProzess.Start();
myProzess.BeginOutputReadLine();
myProzess.BeginErrorReadLine();
myProzess.WaitForExit(600000);
if (numOutputLines > 0)
{
string place = string.Format("{0}\\tmp\\{1}.txt", Environment.CurrentDirectory, arguments);
StreamWriter file = new StreamWriter(place);
stdOutput.Append(Environment.NewLine + Environment.NewLine);
file.Write(stdOutput);
file.Flush();
Thread.Sleep(50);
file.Close();
}
}
else
{
taskError = arguments;
msgError = "Server nicht erreichbar !!!";
}
}
else
{
myProzess.Start();
myProzess.BeginOutputReadLine();
myProzess.BeginErrorReadLine();
myProzess.WaitForExit();
if (numOutputLines > 0)
{
string place = string.Format("{0}\\tmp\\{1}.txt", Environment.CurrentDirectory, arguments);
StreamWriter file = new StreamWriter(place);
stdOutput.Append(Environment.NewLine + Environment.NewLine);
file.Write(stdOutput);
file.Flush();
Thread.Sleep(50);
file.Close();
}
}
args.server = taskError;
args.ErrMsg = msgError;
OnEvent(args);
}
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
protected virtual void OnEvent(prozessReturnArgs e)
{
try
{
myProzess.OutputDataReceived -= new DataReceivedEventHandler(myProzess_OutputDataReceived);
myProzess.ErrorDataReceived -= new DataReceivedEventHandler(myProzess_ErrorDataReceived);
if (prozessCompleted != null)
prozessCompleted(this, e);
}
catch (Exception ex)
{
Console.WriteLine("");
Console.WriteLine("Prozess: " + fileName + " Argumente: " + arguments);
Console.WriteLine("");
Console.WriteLine(ex.Message);
Console.WriteLine("");
}
}
}
public class prozessReturnArgs : EventArgs
{
public string server { get; set; }
public string ErrMsg { get; set; }
}
}
|
PingIP.cs
C#-Code: |
using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Net.NetworkInformation;
namespace FastCmd
{
class PingIP
{
bool status = false;
public bool result
{
get { return status; }
}
public PingIP(string hostName, int timeout)
{
try
{
IPHostEntry host;
IPAddress Adresse = null;
host = Dns.GetHostEntry(hostName);
foreach (IPAddress ip in host.AddressList)
Adresse = ip;
AutoResetEvent waiter = new AutoResetEvent(false);
Ping pingSender = new Ping();
pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);
string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
byte[] buffer = Encoding.ASCII.GetBytes(data);
PingOptions options = new PingOptions(64, true);
pingSender.SendAsync(Adresse, timeout, buffer, options, waiter);
waiter.WaitOne();
}
catch (Exception)
{
status = false;
}
}
public void PingCompletedCallback(object sender, PingCompletedEventArgs e)
{
if ((e.Cancelled)||(e.Error != null))
{
status = false;
((AutoResetEvent)e.UserState).Set();
return;
}
status = true;
((AutoResetEvent)e.UserState).Set();
}
}
}
|
Anregungen und Fragen können unter
[email protected] gestellt werden.
Schlagwörter: FastCMD Admin Betriebsführung Multi Thread Parallel