简体   繁体   English

异步函数冻结 UI 线程

[英]Async function freezes UI thread

I have an async function which still freezes / lags the UI thread for me when I execute it.我有一个异步函数,当我执行它时,它仍然为我冻结/滞后 UI 线程。 This is my function calling it.这是我调用它的函数。

private void TcpListenerLogic(object sender, string e)
        {
            Application.Current.Dispatcher.BeginInvoke((Action)async delegate {
                try
                {
                    dynamic results = JsonConvert.DeserializeObject<dynamic>(e);
                    if (results.test_id != null)
                    {
                        // Get properties for new anchor
                        string testInformation = await CommunicationCommands.getJsonFromURL(
                            "http://" + ServerIP + ":" + ServerPort + "/api/" + results.test_id);
                    }                    
                }
                catch (Exception exception)
                {
                    // Writing some Trace.WriteLine()'s
                }
            });
           
        }

And this is the async function that freezes my UI Thread这是冻结我的 UI 线程的异步函数

        public static async Task<string> getJsonFromURL(string url)
        {
            
            try
            {
                string returnString = null;
                using (System.Net.WebClient client = new System.Net.WebClient())
                {
                    returnString = await client.DownloadStringTaskAsync(url);
                }
                return returnString;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
                return null;
            }

        }

I already tried to make everything in TcpListenerLogic run in a new Thread :我已经尝试使TcpListenerLogic中的所有内容都在新Thread运行:

            new Thread(() =>
            {
                Thread.CurrentThread.IsBackground = true;

            }).Start();

Which resulted in the whole UI completely freezing.这导致整个用户界面完全冻结。 And I tried to make TcpListenerLogic async and await the dispatcher, which also made everything freeze permanently.我尝试使TcpListenerLogic异步并等待调度程序,这也使所有内容永久冻结。 I also tried to make TcpListenerLogic async and leave the dispatcher.我还尝试使TcpListenerLogic异步并离开调度程序。 The dispatcher is only there because I normally have some UI code in there, which I left out for my tests.调度员只是在那里,因为我通常在那里有一些 UI 代码,我在测试中遗漏了这些代码。

I have ventured far through the internet, but no BackgroundWorker , ThreadPool or other methods helped me in my endeavour.我已经通过互联网进行了很远的冒险,但没有BackgroundWorkerThreadPool或其他方法帮助我努力。 If anyone has help for this particular problem, or a resource that would improve my understanding of async functions in C#, I would much appreciate it.如果有人对这个特定问题有帮助,或者有资源可以提高我对 C# 中异步函数的理解,我将不胜感激。

Edit编辑

As requested a deeper insight in how this event handler is called.根据要求更深入地了解如何调用此事件处理程序。 I have System.Net.Websocket, which is connected to the Backend API I am working with and triggers an event, everytime he receives new Data.我有 System.Net.Websocket,它连接到我正在使用的后端 API 并在他每次收到新数据时触发一个事件。 To guarantee the socket listens as longs as it is open, there is a while loop which checks for the client state:为了保证套接字只要打开就一直监听,有一个 while 循环来检查客户端状态:

        public event EventHandler<string> TcpReceived;
        public async void StartListener(string ip, int port, string path)
        {
            try
            {
                using (client = new ClientWebSocket())
                {
                    try
                    {   // Connect to backend
                        Uri serverUri = new Uri("ws://" + ip + ":" + port.ToString() + path );
                        await client.ConnectAsync(serverUri, CancellationToken.None);
                    }
                    catch (Exception ex)
                    {
                        BackendSettings.IsConnected = false;
                        Debug.WriteLine("Error connecting TCP Socket: " + ex.ToString());
                    }
                    state = client.State;
                    // Grab packages send in backend
                    while (client.State == WebSocketState.Open || client.State == WebSocketState.CloseSent)
                    {
                        try
                        {
                            // **Just formatting the received data until here and writing it into the "message" variable**//
                            TcpReceived(this, message);

                            // Close connection on command
                            if (result.MessageType == WebSocketMessageType.Close)
                            {
                                Debug.WriteLine("Closing TCP Socket.");
                                shouldstayclosed = true;
                                await client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                                break;
                            }
                            state = client.State;
                        }
                        catch
                        {
                            BackendSettings.IsConnected = false;
                            state = client.State;
                        }
                    }
                    state = client.State;
                }
            }
            catch (Exception ex)
            {
                // Some error messages and settings handling
            }
        }

The Event has a handler attached:该事件附加了一个处理程序:

TcpReceived += TcpListener_TcpReceived;

And this is the Handler, which calls the previously seen "TcpListenereLogic".这就是处理程序,它调用之前看到的“TcpListenereLogic”。

        private void TcpListener_TcpReceived(object sender, string e)
        {
            TcpListenerLogic(sender, e);

            //App.Current.Dispatcher.BeginInvoke(new Action(() => {
            //      TcpListenerLogic(sender, e);
            //}));

            //new Thread(() =>
            //{
            //    Thread.CurrentThread.IsBackground = true;
            //    TcpListenerLogic(sender, e);
            //}).Start();
        }

I previously had the "TcpListenereLogic" as the handler, but I wanted to try different methods to call it.我以前有“TcpListenereLogic”作为处理程序,但我想尝试不同的方法来调用它。 I also left in the commented out part, to show how the call of "TcpListenereLogic" looked already.我也留在了注释掉的部分,以展示“TcpListenereLogic”的调用看起来如何。 All my attempts were with all mentioned setups and sadly lead to nothing.我所有的尝试都是针对所有提到的设置进行的,遗憾的是没有任何结果。

Thank you very much @TheodorZoulias for helping me to find the solution to my problem.非常感谢@TheodorZoulias 帮助我找到问题的解决方案。

It turns out it wasn't the async function itself, but rather how often it gets called.事实证明,它不是异步函数本身,而是它被调用的频率。 It got called roughly ~120 times every second.它大约每秒被调用约 120 次。 My solution starts by calling the Listener method over a new Thread:我的解决方案首先通过新线程调用 Listener 方法:

new Thread(() =>
{
    Thread.CurrentThread.IsBackground = true;
    MainWindow.tcpListener.StartListener(ip, portNumber, "/api/");
}).Start();

To limit the amount of calls that happen every second I added a dispatcher timer, that resets a bool after it has been used for a call, by my Event.为了限制每秒发生的调用数量,我添加了一个调度程序计时器,它会在我的 Event 用于调用后重置 bool。

readonly System.Windows.Threading.DispatcherTimer packageIntervallTimer = 
                            new System.Windows.Threading.DispatcherTimer();
        bool readyForNewPackage = true;
        private void ReadyForPackage(object sender, EventArgs e)
        {
            readyForNewPackage = true;
        }

        public async void StartListener(string ip, int port, string path)
        {
            packageIntervallTimer.Interval = TimeSpan.FromMilliseconds(50);
            packageIntervallTimer.Tick += (s, e) => { Task.Run(() => ReadyForPackage(s, e)); };
            packageIntervallTimer.Start();

Then I wrapped everything inside the while loop into an if condition based on the bool, the most important part was to have my "event EventHandler TcpReceived" in there:然后我将 while 循环中的所有内容都包装到基于 bool 的 if 条件中,最重要的部分是在那里放置我的“事件 EventHandler TcpReceived”:

// Grab packages send in backend
while (client.State == WebSocketState.Open || client.State == WebSocketState.CloseSent)
{
    if (readyForNewPackage == true)
    {
        readyForNewPackage = false;
        try
        {
           ....
           TcpReceived(this, message);
           ....
        }
        catch
        {
            ...
        }
    }
}

I added my TcpListenerLogic to the Eventhandler:我将我的 TcpListenerLogic 添加到 Eventhandler:

TcpReceived += TcpListenerLogic;

And my TcpListenerLogic now looked like this (names have been changed):我的 TcpListenerLogic 现在看起来像这样(名称已更改):

private async void TcpListenerLogic(object sender, string e)
{
    try
    {
        dynamic results = JsonConvert.DeserializeObject<dynamic>(e);
        if (results.test_id != null)
        {
            string testID = "";
            if (results.test_id is JValue jValueTestId)
            {
                testID = jValueTestId.Value.ToString();
            }
            else if (results.test_id is string)
            {
                testID = results.test_id;
            }

            // Get properties for new anchor
            string information = await CommunicationCommands.getJsonFromURL(
                "http://" + ServerIP + ":" + ServerPort + "/api/" + testID );
            if (information != null)
            {
                await App.Current.Dispatcher.BeginInvoke(new Action(() =>
                {
                    // Create object out of the json string
                    TestStatus testStatus = new TestStatus();
                    testStatus.Deserialize(information);
                    if (CommunicationCommands.isNameAlreadyInCollection(testStatus.name) == false)
                    {
                        // Add new object to the list
                        CommunicationCommands.allFoundTests.Add(testStatus);
                    }
                }));
            {
    }
    catch (Exception exception)
    {
        ....
    }

}

Adding a new Thread to execute any step results in problems, so keep in mind that all this uses the thread created at the beginning for "StartListener"添加一个新线程来执行任何步骤都会导致问题,因此请记住,所有这些都使用在开始时为“StartListener”创建的线程

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM