Как сделать так, чтобы мой пользовательский интерфейс не зависал, когда фоновый код работает на C # - PullRequest
0 голосов
/ 17 июня 2019

Итак, я пытаюсь сделать приложение, которое может выполнять функции, отправленные с клиента, оно работает нормально, но пользовательский интерфейс зависает при прослушивании сообщения от клиента. Что мне нужно изменить, чтобы этот код выполнял Async? Я уже пытался изменить публичный void ExecuteServer (строка pwd) на публичную асинхронную задачу ExecuteServer (строка pwd), но он просто говорит мне, что мне не хватает await

//Where im calling it
public Form2()
{
    InitializeComponent();
    (ExecuteServer("test"));
}

//The Network Socket im trying to run Async        
public static void ExecuteServer(string pwd)
{
    // Establish the local endpoint  
    // for the socket. Dns.GetHostName 
    // returns the name of the host  
    // running the application. 
    IPHostEntry ipHost = Dns.GetHostEntry(Dns.GetHostName());
    IPAddress ipAddr = ipHost.AddressList[0];
    IPEndPoint localEndPoint = new IPEndPoint(ipAddr, 11111);

    // Creation TCP/IP Socket using  
    // Socket Class Costructor 
    Socket listener = new Socket(ipAddr.AddressFamily,
                SocketType.Stream, ProtocolType.Tcp);

    try
    {
        // Using Bind() method we associate a 
        // network address to the Server Socket 
        // All client that will connect to this  
        // Server Socket must know this network 
        // Address 
        listener.Bind(localEndPoint);

        // Using Listen() method we create  
        // the Client list that will want 
        // to connect to Server 
        listener.Listen(10);
        while (true)
        {
            //Console.WriteLine("Waiting connection ... ");

            // Suspend while waiting for 
            // incoming connection Using  
            // Accept() method the server  
            // will accept connection of client 
            Socket clientSocket = listener.Accept();

            // Data buffer 
            byte[] bytes = new Byte[1024];
            string data = null;

            while (true)
            {
                int numByte = clientSocket.Receive(bytes);

                data += Encoding.ASCII.GetString(bytes,
                                        0, numByte);

                if (data.IndexOf("<EOF>") > -1)
                    break;
            }

            Console.WriteLine("Text received -> {0} ", data);
            if(data == "<EOF> " + "kill")
            {
                Application.Exit();
            } 
            else if (data == "<EOF>" + "getpw")
            {
                sendtoclient(clientSocket, pwd);
            } 
            else
            {
                sendtoclient(clientSocket, "Error 404 message not found!");
            }

            // Close client Socket using the 
            // Close() method. After closing, 
            // we can use the closed Socket  
            // for a new Client Connection 
            clientSocket.Shutdown(SocketShutdown.Both);
            clientSocket.Close();
        }
    }

    catch (Exception e)
    {
        //Console.WriteLine(e.ToString());
    }
}

Ответы [ 2 ]

2 голосов
/ 17 июня 2019

Поскольку вы не обращаетесь к пользовательскому интерфейсу внутри серверного цикла и не меняете его, я бы предложил использовать поток.

Вы можете начать новую тему, как это:

public Form2()
{
    InitializeComponent();
    Thread serverThread = new Thread(() => ExecuteServer("test"));
    serverThread.Start();
}

Здесь следует отметить несколько вещей.
Прежде всего, вы никогда не должны запускать долго работающие потоки внутри конструктора. Для этого используйте событие Load. Вы можете создать обработчик событий для него, если дважды щелкнуть форму в конструкторе. Вы также можете сделать что-то вроде этого:

public Form2()
{
    InitializeComponent();
    this.Load += (o, e) => StartServer();
}

private void StartServer() 
{
    Thread serverThread = new Thread(() => ExecuteServer("test"));
    serverThread.Start();
}

Следующее, что следует отметить, это то, что в данный момент у вас нет другого способа остановить поток, кроме отправки правильных данных в сокет. Вы должны по крайней мере использовать volatile bool вместо true во внешнем цикле while.

Также вы должны использовать Application.Exit как можно меньше. С этим решением потока я бы предложил просто выйти из цикла while и выполнить некоторое закрывающее действие в конце метода потока. Ваш ExecuteServer -метод может выглядеть примерно так:

public static void ExecuteServer(string pwd, Action closingAction)
{
    // Establish the local endpoint  
    // for the socket. Dns.GetHostName 
    // returns the name of the host  
    // running the application. 
    IPHostEntry ipHost = Dns.GetHostEntry(Dns.GetHostName());
    IPAddress ipAddr = ipHost.AddressList[0];
    IPEndPoint localEndPoint = new IPEndPoint(ipAddr, 11111);

    // Creation TCP/IP Socket using  
    // Socket Class Costructor 
    Socket listener = new Socket(ipAddr.AddressFamily,
                SocketType.Stream, ProtocolType.Tcp);

    try
    {
        // Using Bind() method we associate a 
        // network address to the Server Socket 
        // All client that will connect to this  
        // Server Socket must know this network 
        // Address 
        listener.Bind(localEndPoint);

        // Using Listen() method we create  
        // the Client list that will want 
        // to connect to Server 
        listener.Listen(10);
        while (_shouldContinue)
        {
            //Console.WriteLine("Waiting connection ... ");

            // Suspend while waiting for 
            // incoming connection Using  
            // Accept() method the server  
            // will accept connection of client 
            Socket clientSocket = listener.Accept();

            // Data buffer 
            byte[] bytes = new Byte[1024];
            string data = null;

            while (true)
            {
                int numByte = clientSocket.Receive(bytes);

                data += Encoding.ASCII.GetString(bytes,
                                        0, numByte);

                if (data.IndexOf("<EOF>") > -1)
                    break;
            }

            Console.WriteLine("Text received -> {0} ", data);
            if (data == "<EOF> " + "kill")
            {
                break;
            }
            else if (data == "<EOF>" + "getpw")
            {
                sendtoclient(clientSocket, pwd);
            }
            else
            {
                sendtoclient(clientSocket, "Error 404 message not found!");
            }

            // Close client Socket using the 
            // Close() method. After closing, 
            // we can use the closed Socket  
            // for a new Client Connection 
            clientSocket.Shutdown(SocketShutdown.Both);
            clientSocket.Close();
        }
    }
    catch (Exception e)
    {
        //Console.WriteLine(e.ToString());
    }

    closingAction();
}

И ваш StartServer должен быть немного скорректирован:

private void StartServer() 
{
    Action closingAction = () => this.Close();
    Thread serverThread = new Thread(() => ExecuteServer("test", closingAction));
    serverThread.Start();
}

Это закроет форму после завершения работы сервера. Конечно, вы можете изменить то действие, которое выполняется.
Также shouldContinue bool должен выглядеть примерно так: private static volatile bool _shouldContinue = true;

Вы, конечно, можете обменять его на свойство или что угодно, просто установите его в false, если хотите, чтобы цикл завершился.

И последнее, имейте в виду, что если вы используете блокирующие вызовы, такие как listener.Accept();, вы, конечно, не будете сразу отменять поток при изменении bool. Для этих вещей я бы посоветовал вам отказаться от блокировки вызовов, как это, и попытаться найти вещи с тайм-аутом, например.

Я надеюсь, что вы можете начать что-то с этим.
Удачи!

EDIT:
При рассмотрении принятого ответа я должен повторить, что вы никогда не должны запускать долго выполняющиеся потоки / задачи внутри конструктора . Если вы действительно хотите использовать async / await вместо задач, не делайте этого так, как предлагает принятый ответ.
Прежде всего, завершение всего тела метода в Task.Run выглядит ужасно и приносит еще больше вложенности. Есть так много способов сделать это лучше:

  1. Используйте локальную функцию и выполните Task.Run для этого.
  2. Используйте отдельную функцию и выполните Task.Run для этого.
  3. Если вы хотите запустить его асинхронно только один раз, и есть варианты использования для выполнения функции синхронно (блокировка), тогда вы должны оставить функцию, подобную этой, и делать Task.Run для нее при вызове.

Также, как упомянуто в моем комментарии под принятым ответом, было бы намного лучше использовать событие Load и сделать так в конструкторе:
Load += async (o, e) => await Task.Run(() => ExecuteServer("test"));.

Это не только устраняет проблему запуска долго выполняющейся задачи внутри конструктора, но и делает асинхронный вызов прямо там, без какого-либо уродливого вложения в функцию ExecuteServer (см. Пункт 3).
Если вы хотите, чтобы функция ExecuteServer была асинхронной сама по себе, см. Пункты 1 и 2.

0 голосов
/ 17 июня 2019

Используйте await Task.Run(() => {...}); в начале ExecuteServer и поместите его код в {...}.

P.S. Перед использованием приведенного выше кода, если вы используете какой-либо компонент из пользовательского интерфейса, вставьте его свойство в переменную. Вот так: var name = txtName.Text; и используйте переменную.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...