Ich erstelle ein Remote-Administration-Tool für meinen Universitätsabschluss. Ich bin momentan wirklich auf einen Fehler in meinem Code fixiert und habe mich gefragt, ob jemand etwas Licht in mein Problem bringen könnte.

Ich habe Anwendungen, einen Server und einen Client. Der Server läuft gut. Der Client ist jedoch die Anwendung, die einfriert.

Vor dem Herstellen einer Verbindung zum Server funktioniert der Client einwandfrei. Bei Verbindung mit dem Server wird der Client ständig auf dem Bildschirm eingefroren.

Ich habe den Fehler auf einen bestimmten Code eingegrenzt. Ohne diesen Code friert die Anwendung nicht ein. Die Anwendung funktioniert jedoch auch nicht.

Hier ist ein Beispiel für diesen Code:

private static void ReceiveResponse()
    {

        var buffer = new byte[2048]; //The receive buffer

        try
        {
            int received = 0;
            if (!IsLinuxServer) received = _clientSocket.Receive(buffer, SocketFlags.None); //Receive data from the server
            else received = _sslClient.Read(buffer, 0, 2048);
            if (received == 0) return; //If failed to received data return
            var data = new byte[received]; //Create a new buffer with the exact data size
            Array.Copy(buffer, data, received); //Copy from the receive to the exact size buffer

            if (isFileDownload) //File download is in progress
            {
                Buffer.BlockCopy(data, 0, recvFile, writeSize, data.Length); //Copy the file data to memory

                writeSize += data.Length; //Increment the received file size

                if (writeSize == fup_size) //prev. recvFile.Length == fup_size
                {


                    using (FileStream fs = File.Create(fup_location))
                    {
                        Byte[] info = recvFile;
                        // Add some information to the file.
                        fs.Write(info, 0, info.Length);
                    }

                    Array.Clear(recvFile, 0, recvFile.Length);
                    SendCommand("frecv");
                    writeSize = 0;
                    isFileDownload = false;
                    return;
                }
            }

            if (!isFileDownload) //Not downloading files
            {
                string text = (!IsLinuxServer) ? Encoding.Unicode.GetString(data) : Encoding.UTF8.GetString(data); //Convert the data to unicode string
                string[] commands = GetCommands(text); //Get command of the message



                foreach (string cmd in commands) //Loop through the commands
                {
                    HandleCommand(Decrypt(cmd)); //Decrypt and execute the command
                }
            }
        }
        catch (Exception ex) //Somethind went wrong
        {
            MessageBox.Show(ex.Message);
            RDesktop.isShutdown = true; //Stop streaming remote desktop
           // MessageBox.Show("Connection ended");
        }
    }

Mit diesem Code erhält der Benutzer eine Anfrage vom Server. Es ist also in einem Timer, der alle 100 ms ausgeführt wird.

Ich frage mich, ob der Timer etwas damit zu tun hat. Vorher war es in einer While (true) -Schleife und ich hatte das gleiche Problem, das mich denken lässt, dass es der eigentliche Code ist, der die Benutzeroberfläche einfrieren lässt.

Der Code funktioniert auch dann noch, wenn die Anwendung eingefroren ist. Abgesehen von der Benutzeroberfläche funktioniert alles in der App.

Das ist wirklich frustrierend und ich kann nichts falsches an dem Code sehen, das dazu führen würde, dass die Anwendung einfriert.

Jede Hilfe wäre sehr dankbar.

Danke im Voraus.

2
Jeremy Griffin 16 Apr. 2018 im 17:03

6 Antworten

Beste Antwort

Also habe ich ein wenig Workaround-Methode dafür ausgearbeitet, habe kein Async verwendet, aber am Ende habe ich einfach einen anderen Thread für diese Aufgabe erstellt.

Mit dem neuen Thread konnte ich eine while (true) -Schleife verwenden, um die Empfangsantwort für immer zu wiederholen.

private void btnConnect_Click(object sender, EventArgs e)
    {
        ConnectToServer();
        Thread timerThread = new Thread(StartTimer);
        timerThread.Start();
        //StartTimer();
        //timerResponse.Enabled = true;

    }
    private static void StartTimer()
    {
        while (true)
        {
            ReceiveResponse();
        }
    }

Das funktionierte gut genug für das, was ich tun musste.

Vielen Dank für die Hilfe!

0
Jeremy Griffin 18 Apr. 2018 im 12:35

Bisher sechs Antworten und keine hilfreichen, korrekten oder umsetzbaren Ratschläge. Ignoriere sie.

Ich habe den Fehler auf einen bestimmten Code eingegrenzt, ohne dass dieser Code, der die Anwendung ausführt, nicht einfriert

Gut, das ist der erste Schritt bei der Diagnose des Problems. Gehen Sie weiter . Welche Methoden hier haben genau die hohe Latenz? Meine Vermutung wäre Receive oder Read, aber es gibt mehrere mögliche Kandidaten, und es könnte mehr als einer von ihnen sein.

Nachdem Sie jede Methode mit hoher Latenz identifiziert haben, bestimmen Sie, ob sie langsam ist, weil sie CPU verwendet oder auf E / A wartet.

Wenn es langsam ist, weil es CPU verwendet, möchten Sie diesen Vorgang auf eine andere CPU verschieben und auf das Ergebnis warten . Sie können dies tun, wie aus den anderen Antworten hervorgeht: Verwenden Sie Task.Run, um die Arbeit in einen Hintergrund-Thread zu verschieben, und dann await das Ergebnis.

Wenn es langsam ist, weil es auf E / A wartet, verschieben Sie den Vorgang nicht in einen Hintergrundthread, es sei denn, Sie haben keine andere Wahl . Die Hintergrund-Worker-APIs dienen zum Auslagern von Arbeit auf andere CPUs, und E / A ist keine CPU-gebundene Arbeit. Hier möchten Sie die asynchronen E / A-Operationen verwenden. Wenn der Blocker beispielsweise Receive ist, verwenden Sie stattdessen ReceiveAsync und await das resultierende Task.

Sie sollten auch Ihre Methode async erstellen und eine Task zurückgeben, die dann vom Aufrufer erwartet wird, und so weiter bis zum Ereignishandler, der das Ganze startet.

Führen Sie diesen Vorgang so lange durch, bis Sie alle Vorgänge in Ihrer Methode identifiziert haben, die länger als 30 Millisekunden dauern, und sie entweder in einem Arbeitsthread asynchronisiert haben, wenn die CPU gebunden ist, oder eine asynchrone E / A-Methode verwendet haben, wenn die E / A gebunden ist. Ihre Benutzeroberfläche sollte dann niemals eine Verzögerung von mehr als 30 ms haben.

Apropos Event-Handler ...

Mit diesem Code erhält der Benutzer eine Anfrage vom Server. Es ist also in einem Timer, der alle 100 ms ausgeführt wird.

Das klingt sehr, sehr falsch. Zunächst "empfängt eine Anfrage vom Server"? Server führen Anfragen im Namen von Clients aus, nicht umgekehrt.

Zweitens klingt dies nach einem falschen Weg, um mit dem Problem umzugehen.

Wenn Sie diesen Code ordnungsgemäß asynchronisieren, sollte kein Timer erforderlich sein, um den Socket ständig abzufragen. Sie sollten einfach asynchron lesen, und entweder ist das Lesen erfolgreich und ruft Sie zurück, oder es tritt eine Zeitüberschreitung auf.

Auch hier gibt es keine Schleife. Sie sagen, dass Sie diesen Code alle 100 ms ausführen und hier maximal 2 KB lesen, sodass Sie maximal 20 KB pro Sekunde lesen, egal wie gesättigt Ihre Netzwerkverbindung ist, oder? Kommt Ihnen das richtig vor? weil es mir falsch erscheint.

3
Eric Lippert 16 Apr. 2018 im 14:53

Sie können BackGroundWorker oder verwenden Sie async/await beide, um das Problem des Einfrierens der Benutzeroberfläche zu beheben.

Der unten angegebene Code ist beispielsweise, dass Sie ihn googeln und mit einem dieser Codes Ihr Problem beheben können


Da es eine Operation gibt, die E / A-Aufrufe ausführt und in neueren Framework-Methoden mit **async verfügbar ist, wird vorgeschlagen, async / awiat zu verwenden

Async / await way (Neuer Weg - .NET 4.5 für 4.0 Version finden Sie nuget)

private async void Button_Click(object sender, RoutedEventArgs 
{
      var task = Task.Factory.StartNew( () => longrunnincode());
      var items = await task;
      //code after task get completed 
}

Back Ground Worker (Alter Weg, aber immer noch unterstützt und in vielen alten Anwendungen verwendet, da async / await in .NET 4.5 für 4.0-Version geliefert wurde, finden Sie Nuget)

private BackgroundWorker backgroundWorker1;
this.backgroundWorker1 = new BackgroundWorker(); 
this.backgroundWorker1.DoWork += new 
        DoWorkEventHandler(this.backgroundWorker1_DoWork);

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundProcessLogicMethod();
}

private void backgroundWorker1_RunWorkerCompleted(object sender,

    RunWorkerCompletedEventArgs e)
{
  if (e.Error != null) MessageBox.Show(e.Error.Message);
    else MessageBox.Show(e.Result.ToString());
} 

private void StartButton_Click(object sender, EventArgs e)

{
    // Start BackgroundWorker
    backgroundWorker1.RunWorkerAsync(2000);
}

Verwenden Sie keine Hintergrund-Worker für E / A, es sei denn, es gibt keinen anderen Weg. Rufen Sie die asynchronen E / A-APIs direkt auf.

1
Pranay Rana 16 Apr. 2018 im 14:23

Es ist wichtiger, in welchem ​​Kontext Sie diesen Code ausführen. Sie führen es wahrscheinlich auf dem UI-Thread aus. Sie sollten schwere Arbeit auf einen anderen Arbeitsthread verschieben oder die Methode asynchronisieren.

-1
oviuan 16 Apr. 2018 im 14:08

Dies ist ein häufiges Problem bei GUIs. Ereignisse sollen so schnell wie möglich beendet werden. Wenn ein Ereignis nicht zurückkehrt, kann kein anderes Ereignis ausgeführt werden. Nicht einmal der Code, der die Benutzeroberfläche mit den letzten Änderungen aktualisiert, wird ausgeführt. Und alles, was Windows dem Benutzer sagen kann, ist, dass das Programm "nicht reagiert" (weil es die letzte Eingabe, die Windows gesendet hat, noch nicht bestätigt hat).

Was Sie tun müssen, ist, die lang laufende Operation in eine Form von Multitasking zu verschieben. Es gibt viele Möglichkeiten, dies zu tun, einige mit mehreren Threads, andere nicht. Wenn ich mit Multitasking in einer GUI-Umgebung beginne (was der Idealfall ist), würde ich die Ansicht BackgroundWorker. Er ist nicht das, was Sie in produktivem Code / später verwenden möchten, aber es ist ein gutes Anfänger-Tool, um die Besonderheiten des Multitasking (Rennbedingungen, Ausnahmebehandlung, Rückrufe) zu lernen.

Da Netzwerkvorgänge normalerweise nicht als CPU-gebunden bekannt sind, sollten Sie auf lange Sicht eine Threadless-Methode für Multitasking wählen.

-1
Christopher 16 Apr. 2018 im 14:13

Es klingt wie es wahrscheinlich der Timer sein wird. Wenn Sie die Methode Thread.Sleep() verwenden, ist dies definitiv das Problem. Die Verwendung dieser Funktion sperrt den aktuellen Thread. Da Ihre Funktion nicht asynchron zu Ihrem UI-Thread ausgeführt wird, friert die UI ebenfalls ein.

Um dieses Problem zu umgehen, können Sie einfach den von Ihnen bereitgestellten Code zu einer asynchronen Aufgabe hinzufügen und mithilfe der Methode Task.Delay() sicherstellen, dass die Funktion nur alle 100 ms ausgeführt wird.

Weitere Informationen finden Sie unter dieser Frage für weitere Informationen.

-1
Harry 16 Apr. 2018 im 14:14