【问题标题】:How to process events fast during a busy process, is there an Update command?如何在繁忙的过程中快速处理事件,是否有更新命令?
【发布时间】:2025-12-13 21:15:02
【问题描述】:

在一个漫长(大约 1 分钟)的过程中,我试图通过将带时间戳的消息写入文本控件来记录一些进度。但是所有消息都会同时出现。显然,所有PropertyChanged events 都在排队​​,直到我忙碌的过程完成,并被文本控件一次性接收。我怎样才能在忙碌的过程中“刷新”事件?我进行了搜索,但找不到用于立即处理排队事件的 Flush/Update/Dispatch 调用。

question 1194620 中有一个多线程解决方案,但我首先想尽可能避免使用多线程。在较旧的环境(C++、.Net Winforms/ASP)中,总是有像Update 这样的系统调用来中断繁忙的进程以处理未决事件。

编辑:请不要告诉我一个冗长的过程应该在另一个线程中。我同意。但这是继承的代码,在我考虑转换为多线程之前,我首先需要记录某些事件以了解它的作用。此外,这个应用程序还有许多其他问题需要先解决。另外,解决问题后,漫长的过程可能不再漫长。

我在question 18888937 中找到的解码代码中任何地方写入字符串的方法,效果很好。

这是代码隐藏。 编辑:我在接受的答案中添加了对解决方案的调用。

public partial class App : Application, INotifyPropertyChanged
{
    /// <summary>
    /// Property for the log message for the TextBlock control
    /// </summary>
    public string StartupMessage
    {
        get { return _StartupMessage; }
        set
        {
            if (_StartupMessage.Length == 0)
            {
                _StartupMessage = string.Format("{0:HH-mm-ss} {1}", 
                                          DateTime.Now, value);
            }
            else
            {
                _StartupMessage = string.Format("{0}{1}{2:HH-mm-ss} {3}",
                  _StartupMessage, Environment.NewLine, DateTime.Now, value);
            }
            OnPropertyChanged("StartupMessage");
        }
    }
    private string _StartupMessage = "";

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
            DoEvents();//see the accepted answer below
        }
    }

这是文本控件:

<TextBlock x:Name="textblock_StartupMessages" 
             Margin="10" TextWrapping="Wrap" 
  Text="{Binding Path=StartupMessage, Source={x:Static Application.Current}}">
</TextBlock>

下面是我在代码中放置来自其他地方的消息的方式:

public class AllRoutesViewModel : ViewModelBase
{
    public AllRoutesViewModel()
    {
        (System.Windows.Application.Current as App).StartupMessage = 
                                           "start of AllRoutesViewModel()";

【问题讨论】:

  • 您在寻找Dispatcher.PushFrame吗?
  • 你为什么不在后台线程中运行冗长的操作?
  • 你不想这样做:msdn.microsoft.com/en-us/library/…,因为它很糟糕。但是,由于您似乎决心避免使用多线程方法,我认为这可能是您唯一的选择。
  • @YuvalItzchakov 你是对的,但这是继承的代码,重新设计它并非易事。无论如何,我现在处于分析阶段,需要一个日志工具。理想情况下,我会修复一些瓶颈并缩短流程,多线程不会使其更快。
  • @Roland 我并没有暗示让您的应用程序多线程会使其更快。它做的是释放 UI 消息循环来处理传入的消息,例如您的 NotifyPropertyChanged

标签: c# wpf .net-4.0 inotifypropertychanged


【解决方案1】:

尽可能避免使用多线程。在较旧的环境中(C++、.Net Winforms / ASP)总是有像更新这样的系统调用来中断 处理未决事件的繁忙进程。

这是在一个系统上尝试一种设计模式,该系统的设计不像你提到的系统。

不应在 WPF 中的 GUI 线程上执行长时间运行的操作。

通知属性更改仅在 GUI 线程未被阻塞时才有效,因为它本质上是一个 GUI 进程。您拥有的代码阻塞了 GUI 线程。如果您正确在后台工作程序或异步任务中运行任务并正确更新您的属性,则通知将使 GUI 在视觉上按照您实际想要和期望的方式运行。

但是根据您呈现的设计,以图形方式做到这一点是不可能的。最好的答案是学习 WPF 设计模式并遵循它,而不是强制采用不同的技术设计模式。

【讨论】:

  • +1 作为结束语。 OP 应该停止尝试将方形钉子敲入圆孔。
  • 完美讨论 vrijmibo(荷兰语:vrijdag middag borrel,英语:Friday Afternoon Party)和一些啤酒。避免多线程:在 A. Einstein K.I.S.S. 之后设计模式:同意。不可能:不,它有效,感谢 Okuma Scott。重新设计这个继承的代码:太好了,如果我的老板允许我的话。代码很烂:我们都有这样的经历。但这都是题外话。我有一个有效的问题,得到了一个不错的解决方案,挽救了我的一天。周末愉快。
  • @Roland 据了解,您在现有代码的限制下工作,这有时可悲地胜过正确的设计。
  • @OmegaMan sadly 是的,但是gladly 它让我能够谋生。但是您应该正确看待这一点,继承的代码比这个 MT/Update 问题要悲惨得多,而我认为用这个代码解决问题是为了赚钱而玩了一场伟大的游戏。感谢大家今天帮助我并激励我保持良好的设计。
【解决方案2】:

您可以考虑使用Dispatcher.PushFrame

更多关于课程here的信息。

另外,这里是来自 MDSN 的相关代码示例(稍作修改):

using System.Windows.Threading;  //DispatcherFrame, needs ref to WindowsBase

//[SecurityPermissionAttribute(SecurityAction.Demand, Flags =   SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(ExitFrame), frame);
    Dispatcher.PushFrame(frame);
}

public object ExitFrame(object f)
{
    ((DispatcherFrame)f).Continue = false;

    return null;
}

虽然在这种情况下这个解决方案可能会满足您的需求,但我必须同意其他人对设计模式的看法。以后请考虑MVVM 之类的东西。

【讨论】:

  • 谢谢,这行得通。该链接适用于 .Net4.5,但也支持 .Net4。我添加了使用。另外,SecurityPermissionAttribute 在我的项目中没有被识别,但它也可以正常工作,所以我把它注释掉了。