为了账号安全,请及时绑定邮箱和手机立即绑定

当后台代码运行 C# 时,如何使我的 UI 不冻结

当后台代码运行 C# 时,如何使我的 UI 不冻结

C#
斯蒂芬大帝 2023-04-16 09:51:06
因此,我试图制作一个可以执行从客户端发送的功能的应用程序,它工作正常,但 UI 在侦听来自客户端的消息时冻结,我必须更改什么才能使此代码异步运行?已经尝试将 public void ExecuteServer(string pwd) 更改为 public async task ExecuteServer(string pwd) 但它只是告诉我我缺少等待//Where im calling itpublic 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;            }
查看完整描述

2 回答

?
BIG阳

TA贡献1859条经验 获得超6个赞

由于您没有在服务器循环中访问或更改 UI,我建议使用线程。


您可以像这样启动新线程:


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

}

接下来要注意的是,除了将正确的数据发送到套接字之外,您目前无法停止线程。您至少应该在外部 while 循环中 使用 avolatile bool而不是 the 。true


你也应该Application.Exit尽可能少地使用。对于这个线程解决方案,我建议只是跳出 while 循环并在线程方法结束时执行一些关闭操作。你的ExecuteServer-method 看起来像这样:


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应该是这个样子: private static volatile bool _shouldContinue = true;


如果您希望循环结束,您当然可以将其交换为属性或任何您想要的,只需将其设置为 false 即可。


最后一件事,请记住,如果您正在使用阻塞调用,listener.Accept();您当然不会在更改 bool 时立即取消线程。对于这些事情,我建议您远离这样的阻塞调用,并尝试查找超时的事情。


我希望你能以此为起点。

祝你好运!


编辑:

在考虑接受的答案时,我必须重申,你永远不应该在构造函数中启动长时间运行的线程/任务。如果你真的想使用 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 点。


查看完整回答
反对 回复 2023-04-16
?
慕姐8265434

TA贡献1813条经验 获得超2个赞

await Task.Run(() => {...});在ExecuteServer的开头使用,将它的代码放在里面{...}

PS 在使用上面的代码之前,如果您使用 UI 中的任何组件,请将其属性插入变量中。像这样:var name = txtName.Text;并使用变量。



查看完整回答
反对 回复 2023-04-16
  • 2 回答
  • 0 关注
  • 83 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信