简体   繁体   English

如何在后台代码运行C#时使UI不冻结

[英]How do I make my UI not Freeze while background code is running C#

So Im trying to make an app that can execute functions send from a client it works fine but the UI freezes while its listening for a message from a Client what do I have to change to make this code run Async? 因此,我试图制作一个可以执行从客户端发送的功能的应用程序,但工作正常,但UI冻结,同时侦听来自客户端的消息时,我需要更改些什么才能使此代码运行Async? Already tried changing public void ExecuteServer(string pwd) to public async task ExecuteServer(string pwd) but it just tells me that im lacking an await 已经尝试将public void ExecuteServer(string pwd)更改为public async task ExecuteServer(string pwd),但这只是告诉我即时通讯缺乏等待

//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());
    }
}

Since you're not accessing or changing the UI within the server-loop I would suggest using a Thread. 由于您不在服务器循环中访问或更改UI,因此建议您使用线程。

You could start the new Thread like this: 您可以像这样启动新线程:

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

A few things to note here though. 不过,这里需要注意几件事。
First of all, you should never start long running threads inside the constructor. 首先,永远不要在构造函数内部启动长时间运行的线程。 Use the Load event for that. 为此使用Load事件。 You can create a eventhandler for it if you double click on the form in the designer. 如果双击设计器中的表单,则可以为其创建事件处理程序。 You can also do something like this: 您还可以执行以下操作:

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

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

Next thing to note would be that you currently have no way of stopping the thread other than sending the right data to the socket. 接下来要注意的是,除了将正确的数据发送到套接字之外,您目前无法停止线程。 You should at least use a volatile bool instead of the true in the outer while loop. 您至少应在外部while循环中使用volatile bool而不是true

Also you should use Application.Exit as little as possible. 另外,您应该尽可能少地使用Application.Exit With this thread solution, I would suggest just breaking out of the while loop and executing some closing action at the end of the thread-method. 使用这种线程解决方案,我建议您只是打破while循环,并在线程方法的末尾执行一些关闭操作。 Your ExecuteServer -method could look something like this: 您的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();
}

And your StartServer would have to be adjusted a bit: 而且您的StartServer必须进行一些调整:

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

This will close the form once the server has ended. 服务器结束后,将关闭表单。 Of course you can change that action which is executed. 当然,您可以更改执行的动作。
Also the shouldContinue bool should look something like this: private static volatile bool _shouldContinue = true; 另外, shouldContinue bool应该看起来像这样: private static volatile bool _shouldContinue = true;

You can of course exchange that for a property or whatever you want just set it to false if you want the loop to end. 当然,您可以将其交换为属性,或者如果希望循环结束,只需将其设置为false即可。

Last thing, keep in mind that if you're using blocking calls like listener.Accept(); 最后,请记住,如果您正在使用诸如listener.Accept();类的阻塞调用listener.Accept(); you will of course not cancel the thread straight away when changing the bool. 您当然不会在更换布尔管时立即取消螺纹。 For these things I would advise you to move away from blocking calls like this and try to find things with timeout for example. 对于这些事情,我建议您远离阻塞这样的呼叫,并尝试例如在超时的情况下进行查找。

I hope you can start something with this. 我希望您可以从这里开始。
Good Luck! 祝好运!

EDIT: 编辑:
When considering the accepted answer I have to repeat that you should never start long running threads/tasks inside a constructor . 在考虑可接受的答案时,我不得不重复一遍, 您永远不要在构造函数内部启动长时间运行的线程/任务 If you really want to use async/await instead of tasks please don't do it like the accepted answer suggests. 如果您真的想使用async / await代替任务,请不要像接受的答案所建议的那样做。
First of all wrapping the whole method-body in a Task.Run looks horrible and brings a layer of nesting more. 首先,将整个方法主体包装在Task.Run看起来很可怕,并带来了更多的嵌套层。 There are so many ways you could do this better: 有很多方法可以更好地做到这一点:

  1. Use a local function and execute Task.Run on that. 使用本地函数并执行Task.Run
  2. Use a separate function and execute Task.Run on that. 使用一个单独的函数并执行Task.Run
  3. If you only want to start it asynchronously once and there are use cases for executing the function synchronously (blocking) then you should keep the function like this and do Task.Run on it when you're calling it. 如果您只想异步启动一次,并且有一些同步执行(阻塞)功能的用例,那么您应该保持这样的功能并在调用它时执行Task.Run

Also as mentioned in my comment under the accepted answer, it would be much better to use the Load event and do like this in the constructor: 另外,正如我在接受的答案下的评论中所述,最好使用Load事件并在构造函数中执行以下操作:
Load += async (o, e) => await Task.Run(() => ExecuteServer("test")); .

Not only fixes this the problem of starting a long running task inside the constructor, but it also makes the call asynchronous right there without any ugly nesting inside the ExecuteServer function (see point 3). 不仅解决了在构造函数内部启动长时间运行的任务的问题,而且还使调用在那里异步进行,而ExecuteServer函数内部没有任何难看的嵌套(请参见第3点)。
If you want the ExecuteServer function to be asynchronous on its own, see point 1 and 2. 如果希望ExecuteServer函数自己实现异步,请参见第1点和第2点。

Use await Task.Run(() => {...}); 使用await Task.Run(() => {...}); at the beginning of ExecuteServer and put its code inside {...} . 在ExecuteServer的开头,并将其代码放在{...}

PS Before using the above code, if you're usging any component from UI, insert it's property in a variable. PS在使用上述代码之前,如果要使用UI中的任何组件,请将其属性插入变量中。 Like this: var name = txtName.Text; 像这样: var name = txtName.Text; and the use the variable. 和使用变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何允许我的.net程序在运行时编译C#代码? - How do I allow my .net program to compile C# code while running? 如何在WPF的主UI线程中运行长时间运行的进程时锁定UI(C#中的代码) - How can I lock UI while running the long running process in the main UI Thread in WPF (code in C#) 如何在C#中的UI线程上延迟运行代码? - How can I delay running code on the UI thread in C#? C# 线程/异步:在 UI 可交互时在后台运行任务 - C# Threading/Async: Running a task in the background while UI is interactable 在单击提交按钮时保持我的进度条运行并冻结屏幕和后台代码应该运行 - To Keep my progress bar running while clicking submit button and to freeze the screen and the background code should run 如何从我的c#代码后面打开jQuery UI对话框? - How do I open a jQuery UI Dialog from my c# code behind? 如何从我的 blazor (c#) 代码中连接到 c# web api,两者都在 gitpod 上运行? - How do I connect to the c# web api out of my blazor (c#) code, both running on gitpod? 如何用代码替换面板上的背景图像? C# - How do I replace a background image on a panel in code? C# 如果 C# .NET 出现错误,如何让我的代码跳过某些内容 - How do I make my code skip something if there is an error C# .NET 如何使用 C# 中的代码进行单元测试 - How do I make a unit test out of my code in C#
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM