C# Wie kann ich die Kommandozeile als externen Logger misbrauchen?

Kokujou

Lieutenant
Registriert
Dez. 2017
Beiträge
948
Folgendes: Ich versuche gerade mit Multithreading anzufangen. Leider Gottes scheint Unity3D das mit dem Debugging dabei nicht so zu mögen. Wenn ich die dortigen Funktionen verwende, zeigt er sie erst an wenn die Aufgabe abgeschlossen ist. Was doof ist wenn die gefühlt 5 STunden dauert... Nicht nur gefühlt

Der code wird aber ausgeführt. Er wird nur nicht angezeigt. Das bedeutet ich versuche nun über System.Diagnostics.Process die CMD zu öffnen und da Befehle reinzupasten. Meine ersten Versuche waren damit auch erfolgreich, jetzt muss es nur noch schön aussehen.

echo off erledigt ja schonmal das erste und it cls kann ich alles darüber löschen. Mein Problem ist, dass alles was ich zur KOmmandozeile umleite auch ausgeführt wird.

Also zwei Lösungsansätze von denen ich nicht weiß wie sie funktionieren:
1. Ich kriege es hin, dass die Eingabe zur Kommandozeile umgeleitet wird ohne dass die Zeile beendet und der Befehl ausgeführt wird oder
2. Ich kriege es hin dass die Befehle die zur Ausgabe führen nicht angezeigt werden.

Ich will quasi dass es am Ende so aussieht:
1547900420828.png
 
Die Fragen verstehe ich nicht. Zeile beendet? Befehl ausgeführt? Hä? Ein .log(xy) ist natürlich immer ein separater "Befehl". Vielleicht mal Beispielcode zeigen.

aLca hat denke ich schon das richtige verlinkt. Selber bauen ist aber auch nicht schwierig.
Für´s dev debugging ist Console.WriteLine u.ä. bereits thread-safe. Da kannst du dir das cmd echo sparen (wozu?). Für´s Datei-Logging mal anschauen, was lock macht und TextWriter.Synchronized.
 
.... lock würde zu stark verlangsamen ich hab hier wirklich ein Haufen Prozessorleistung die ich brauche.

Unitys Debug.Log gibt in die Unity Konsole aus. Aber der Main Thread ist Blockiert weil er ja darauf wartet das meine Millionen von Threads fertig werden. die Debug.Logs werden zwar ausgeführt aber eben nicht angezeigt weil das Fenster in der Zeit einfriert.

Also leite ich die Ausgabe in ne Konsole um indem ich System.Diagnostics.Process die CMD starte und den StandardInput beschreibe.
 
Kokujou schrieb:
Aber der Main Thread ist Blockiert weil er ja darauf wartet das meine Millionen von Threads fertig werden.
Genau da sollteste mal ansetzen. Zu viele Threads sind kontraproduktiv. Die Devs von der CryEngine (oder wars doch von Unreal?) haben das ganz gut umrissen irgendwo.
 
Code:
System.Diagnostics.Debug.WriteLine("Text")
// Oder für Release:
System.Diagnostics.Trace.WriteLine("Text")
Wäre eine Alternative.
Mit DebugView lassen sich die Ausgaben dann in Echtzeit anschauen.

Wenn Du viele Threads hast, die nur kurz was machen (unter 10s), benutze besser die TPL. Threads zu erzeugen ist ein riesiger Overhead fürs System. Bei der TPL werden Threads aus dem ThreadPool recycled. Das System ist extrem gut optimiert:
Code:
Task.Run(()=>
{
// Code hier
});
Das kann bei vielen kurzen Aufgaben um ein tausendfaches schneller sein als Threads zu erzeugen.
 
Das verwende ich sogar... Es ist Task.Start aber sonst...

Das mit dem Multithreading ist viel komplizierter als es sein sollte kann man auf neue Tasks überhaupt warten?
ich hab erstmal Multithreading mit dem ganzen Async-Zeug verwechselt und jetzt machen meine tasks gleich mal gar nichts mehr als ichs umgebaut habe XD

vielleicht geht beides parallel ja auch gar nicht...
 
Kokujou schrieb:
Multithreading mit dem ganzen Async-Zeug verwechselt
Yep, ist mir am Anfang auch passiert :D. Bei Async gehts eher darum einen einzelnen Thread optimal zu nutzen.

Kokujou schrieb:
kann man auf neue Tasks überhaupt warten?
Ich weiß nicht genau wie Du das meinst. Die Tasks werden beim Erzeugen in die Warteschlange vom Threadpool gelegt und dann abgearbeitet. Man muss nicht auf neue freie Tasks warten.
Wenn Du eher sowas wie Thread.Join() meinst, dann brauchst Du nur eine Referenz auf den Task zu speichern. Das Task Objekt hat dann entsprechende Eigenschaften um abzufragen ob er schon fertig ist, z.B. IsCompleted. Join geht z.B. mit Wait(). Dann gibts noch ContinueWith() um eine weitere Aufgabe im Anschluss auszuführen und das Resultat mitzunehmen.
 
Naja es ist auf jeden fall eine riesen Fummelei das alles gleichzeitig zu nutzen... Asynchron und parallel zu arbeiten^^

Aber darum gehts ja hier nicht. Hier gehts darum irgendwie ne Ausgabe auf ner Konsole zu erzeugen ohne dass das unnütze drumrum angezeigt wird.

Aktuell bin ich dabei dass ich an jede Zeile ^ anhänge, das kann man machen aber jetzt steht vor jeder Zeile Mehr?

Gut es ist nur fürs Debugging aber es wär halt interessant zu wissen wie andere das hingekriegt haben. aber da muss man wohl n extra programm für schreiben XD
 
Momeeeent.... ich glaube ich verstehe jetzt was Du machst.
Du öffnest ein Konsolenfenster und schreibst da manuell Text per Copy Paste rein?
Klar, der Text wird als Kommandozeilenbefehl ausgeführt sobald die Zeile umgebrochen wird.

Wenn Du eine Konsole öffnen willst um Debugausgaben zu schreiben geht das anders. Ich hatte da mal was geschrieben. Ich schau kurz ob ich das noch habe...

Edit:
So geht das:
AllocConsole() aufrufen und dann ganz normal Console.WriteLine().
In Visual Studio wird die Konsolenausgabe umgeleitet. Kann sein, dass während dem Debuggen dadurch nichts angezeigt wird. Wenn das Programm alleine ausgeführt wird, landet aber alles in der Konsole.
C#:
using System.Runtime.InteropServices;

namespace WpfApp1
{
    class DebugKonsole
    {
        [DllImport("Kernel32")]
        public static extern void AllocConsole();

        [DllImport("Kernel32")]
        public static extern void FreeConsole();


        bool _istOffen;
        public void Oeffnen()
        {
            if (_istOffen) return;
            AllocConsole();
            _istOffen=true;
        }

        public void Schliessen()
        {
            if (!_istOffen) return;
            FreeConsole();
            _istOffen = false;
        }

        public void SchreibeZeileInKonsole(string text)
        {
            System.Console.WriteLine(text);
        }

    }
}

Mit der ConsoleHelper Klasse von hier gehts sogar unter Visual Studio:
https://stackoverflow.com/questions...directing-console-output-to-the-output-window
 
Zuletzt bearbeitet:
Neiiiiiiiiiiin! Ich hab doch schon gesagt wie ich es mache.
Ich rufe Sytem.Diagnostics.Process.StandardInput.WriteLine auf
Aber der tut eben genau so als hätte ich nur copy Paste rein gemacht oder so. Nur sehr viel schneller.

Ich hab den Link schon in ner anderen Frage zu mienem Multithreading aber dann hier nochmal: StateTree.cs

Wichtig ist dass es ein neuer Prozess ist! Denn Unity ist eingefroren in der Zeit und erzeugt die Ausgabe erst wenns zu spät ist. Ich will Live-Feedback a la Multithreading Debugging.
 
Achso ja stimmt. Sorry.
Dann müsste eigentlich meine Lösung mit DebugView, die ich oben hatte gehen. Da ist die Ausgabe live. Ansonsten müsstest Du wirklich ein Konsolenprogramm schreiben was den StandardInput in einer Schleife liest und wieder rausschreibt. Sind nur ein paar Zeilen Code.
 
Zuletzt bearbeitet:
aaaaaah den Link hatte ich übersehen... aber ich wollts halt cool mit Konsole machen weil ich das auch immer in anderen Programmen sehe wie die ein Konsolenfester dazu haben. und dachte so geht es schnell und schmerzlos^^
 
OK. Habs jetzt trotzdem mal selber mit einem Konsolenprogramm probiert weil ich wissen wollte obs geht:

Diesen Code als Konsolenprogramm speichern (ConsoleApp1.exe).
C#:
class Program
{
    static void Main(string[] args)
    {
        string line = "";
        while (true)
        {
            line = Console.ReadLine();
            Console.WriteLine(line);
        }
    }
}

Das hier im Hauptprogramm benutzen:
C#:
public class Konsole
{
    Process _p;
    public void Open()
    {
        Close();
        _p = System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo()
        { FileName = "ConsoleApp1.exe", RedirectStandardInput = true, UseShellExecute = false });
    }
    public void Close()
    {
        try { _p?.Kill(); } catch { /*todo*/ }
        _p = null;
    }

    public void Log(string text)
    {
        try { _p?.StandardInput.WriteLine(text); } catch { /*todo*/ }
    }
}

Hier ist mein Beispielaufruf (ist ein WindowsForms Fenster mit einem Button):
C#:
private void button1_Click(object sender, EventArgs e)
{
    var k = new Konsole();
    k.Open();

    // Fenster einfrieren für 5 Sekunden und Zeug ausgeben:
    for (int i = 0; i < 5; ++i)
    {
        Thread.Sleep(1000);
        k.Log(i.ToString());
    }
    k.Close();
}

Läuft. Aber Wenn Visual Studio an ist, landet die Ausgabe von der Konsole im Output Fenster von Visual Studio.
 
OK in der VS-Konsole landet sie wahrscheinlich nicht... eher in der Unity Konsole. Und da ist das Problem. Unity friert nämlich wie gesagt ein^^ Und wenn Unity einfriert gibts keine Grafikänderungen also keine Anzeige auf der Konsole. Ich find ja auch dass das total dämlich ist.

Also brauche ich nen Prozess dessen Main-Thread auf dem auch die Grafik-Updates laufen nehme ich an, nicht blockiert ist.
 
Weiß jetzt nicht genau wie der Main Thread von Unity funktioniert. Du musst auf jedenfall irgendwas machen, damit BuildStateTree die Arbeit in einem Hintergrundthread erledigt.
Habe selber bei meinem Brettspiel auf GitHub auch eine KI, die verschiedene Zustände im Vorraus berechnet und dann den besten spielt. Das kann auch etwas dauern, aber weil die KI bei mir im Hintergrund läuft, blockiert da im Hauptthread nichts.

Das async in Deiner KI Klasse kannst Du wieder rausmachen. In Deinem Fall erzeugt das nur unnötigen Overhead. Der Compiler erzeugt bei "await" intern zusätzlichen Code (eine State Machine um genau zu sein). Um es kurz zu fassen, der Code wird dadurch eher langsamer.

Ansonsten:
Ich hab echt zu viel Freizeit :D. Hier ist ein Programm, das einfach einen zweiten Hauptthread erzeugt und damit ein WindowsForm Fenster fürs Logging aufmacht. In ein Unity Programm würde ich das jetzt vielleicht nicht reinmachen, aber vorrübergehend zum Debuggen kann man es nehmen.
dbglog.png

C#:
// Referenz zu System.Windows.Forms.dll einfügen
using System;
using System.Threading;
using System.Windows.Forms;

namespace ConsoleApp3
{
    class Program
    {
        static void Main(string[] args)
        {
            var win = new LogWindow();

            Thread.Sleep(1000);
            win.Log("Hello");
            Thread.Sleep(1000);
            win.Log("World!");
            Thread.Sleep(1000);

            Console.WriteLine("Drücke beliebigen Knopf um Fenster zu schließen");
            Console.ReadKey();
            win.Dispose();
            Console.WriteLine("Drücke beliebigen Knopf um Programm zu beenden");
            Console.ReadKey();
        }
    }

    class LogWindow : IDisposable
    {
        LogContext _context;
        Thread _staThread;

        class LogContext : ApplicationContext
        {
            WindowsFormsSynchronizationContext _context;
            Form _mainWindow;
            TextBox _box;
            public bool Disposed { get; private set; }
            public bool Initialized { get; private set; }

            public void Log(string text)
            {
                if (Disposed) return;
                _context.Post(o => { _box.AppendText(text + "\r\n"); _box.ScrollToCaret(); }, null);
            }

            public LogContext()
            {
                _context = new WindowsFormsSynchronizationContext();
                SynchronizationContext.SetSynchronizationContext(_context);
                SynchronizationContext.Current.Post((o =>
                {
                    _mainWindow = new Form() { ShowIcon = false, Text = "Debug Log" };
                    _box = new TextBox() { Multiline = true, Dock = DockStyle.Fill };
                    _mainWindow.Controls.Add(_box);

                    _mainWindow.Show();
                    _mainWindow.FormClosed += (s,a) => { _context.Dispose(); this.Dispose(); Disposed = true; };
                    Initialized = true;
                }), null);
            }
        }

        public LogWindow()
        {
            _staThread = new Thread(() =>
            {
                _context = new LogContext();
                Application.Run(_context);
            });
            _staThread.SetApartmentState(ApartmentState.STA);
            _staThread.Start();
            while (_context?.Initialized != true) { Thread.SpinWait(1); }
        }

        public void Log(string text)
        {
            _context.Log(text);
        }

        bool _disposed;
        public void Dispose()
        {
            if (_disposed) return;
            _context.ExitThread();
            _context.Dispose();
            _disposed = true;
        }
    }

}
 
Naja aber meine berechnet das ja für jeden Zug und nicht am anfang um weiter aufzubauen. Das wär natürlich auchn ansatz um das maximum rauszuholen aber ich brauchs halt für jeden Zug und kalkuliere da das tiefste was möglich ist...
Das wird dann natürlich immer schneller weil ja nicht mehr so viele karten übrig sind aber jo...
aktuell dauert ein normales aufbauen mit ignorieren der gegnerischen Züge 25 sekunden im worst case...

Danke! XD
 
Für mich geht das glaub gar nicht... Unity hat Windows Forms nämlich gar nicht drinne :P aber im Prinzip sagst du damit ja: Schreib dirn Extra Programm dass die Ausgabe so macht wie du willst und leite die ausgabe darin um.

Das kannich ja auch als neuen Prozess wie gehabt starten und dann die Ausgabe beschreiben. Am besten ne Konsolenanwendung.
 
Zurück
Oben