[英]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.我已经通过互联网进行了很远的冒险,但没有
BackgroundWorker
、 ThreadPool
或其他方法帮助我努力。 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.