在其他线程中触发事件后在事件处理程序中更新 UI 的最佳方法
Best way of updating UI in event handler after fire event in other thread
我的项目需要按事件更新一些 UI 元素,并且一些事件在异步或后台线程中触发。所以我需要在事件处理程序中添加一些线程安全的代码。
但是我发现项目中有很多相同的情况。这些零散的线程安全代码让整个项目代码变得相当丑陋,希望有办法改进。
下面是其他线程中触发事件的示例代码:
public static class MyAsyncClass
{
public static event Action MyEvent;
public static void AsyncCallEvent()
{
//...
Task.Run(() => {
while(runningFlag)
{
//...
if(flag)
MyEvent?.Invoke();
}
});
}
}
public class MyWindow : Window
{
//...
private void OtherMethod()
{
UpdateText();
}
private void Init()
{
MyAsyncClass.MyEvent += UpdateText;
}
private void UpdateText()
{
//Do update
}
//...
}
这里有两种updateText代码:
//Clear, but not cross-thread safety
//will fail in cross-thread event invoking
private void UpdateText()
{
MyTextBox.Text = "NewText";
}
//Cross-thread safe, but make code noisy
private void UpdateText()
{
Dispatcher.Invoke(() => MyTextBox.Text = "NewText");
}
当然,第二个代码肯定更好,但是如果将它添加到所有可能被异步调用的事件处理程序中,代码将变得令人不愉快。
是否有更清洁的解决方案?
好吧,您可以在调度程序线程上调用 UpdateText()
本身:
private void Init()
{
MyAsyncClass.MyEvent += () => Dispatcher.Invoke(UpdateText);
}
private void UpdateText()
{
MyTextBox.Text = "NewText";
}
但是您的代码真正丑陋的是您使用 Task.Run
在 MyAsyncClass
中的另一个线程上显式执行事件处理程序。您应该在引发事件的同一线程上调用事件处理程序。然后由事件的订阅者选择是否将事件处理程序中的代码卸载到另一个线程。
我的项目需要按事件更新一些 UI 元素,并且一些事件在异步或后台线程中触发。所以我需要在事件处理程序中添加一些线程安全的代码。
但是我发现项目中有很多相同的情况。这些零散的线程安全代码让整个项目代码变得相当丑陋,希望有办法改进。
下面是其他线程中触发事件的示例代码:
public static class MyAsyncClass
{
public static event Action MyEvent;
public static void AsyncCallEvent()
{
//...
Task.Run(() => {
while(runningFlag)
{
//...
if(flag)
MyEvent?.Invoke();
}
});
}
}
public class MyWindow : Window
{
//...
private void OtherMethod()
{
UpdateText();
}
private void Init()
{
MyAsyncClass.MyEvent += UpdateText;
}
private void UpdateText()
{
//Do update
}
//...
}
这里有两种updateText代码:
//Clear, but not cross-thread safety
//will fail in cross-thread event invoking
private void UpdateText()
{
MyTextBox.Text = "NewText";
}
//Cross-thread safe, but make code noisy
private void UpdateText()
{
Dispatcher.Invoke(() => MyTextBox.Text = "NewText");
}
当然,第二个代码肯定更好,但是如果将它添加到所有可能被异步调用的事件处理程序中,代码将变得令人不愉快。
是否有更清洁的解决方案?
好吧,您可以在调度程序线程上调用 UpdateText()
本身:
private void Init()
{
MyAsyncClass.MyEvent += () => Dispatcher.Invoke(UpdateText);
}
private void UpdateText()
{
MyTextBox.Text = "NewText";
}
但是您的代码真正丑陋的是您使用 Task.Run
在 MyAsyncClass
中的另一个线程上显式执行事件处理程序。您应该在引发事件的同一线程上调用事件处理程序。然后由事件的订阅者选择是否将事件处理程序中的代码卸载到另一个线程。