c# - 在一种情况下 Dispatcher.BeginInvoke 未执行

标签 c# .net multithreading dispatcher

我正在尝试修复现有应用程序中的一个错误,该错误在单一情况下未执行分派(dispatch)的事件。

在我们的应用程序中,有多个服务实现一个公共(public)抽象类(AbstractService)。当服务保存实体并且必须在 UI 线程中发出通知时,就会出现我遇到的问题。

AbstractService的相关部分:

public abstract class AbstractService<TEntity, TData, TInfo> : IDataProvider, IAbstractService
    where TEntity : AbstractEntity, new()
    where TData : AbstractData<TEntity>
    where TInfo : IEntityInfo {

    [...]

    protected virtual void NotifyEntityChanged(NotifyEventItem<TInfo>[] pNotifyEventItems) { }

    private void NotifyPersPlanChanged() {
        if (PersPlanChanged != null)
            PersPlanChanged(this, new NotifyEventArgs<IEntityInfo>(null, PersistanceState.Reset));

        Debug.WriteLine(string.Format("{0} {1} Call {2} to NotifyPersPlanChanged",
            DateTime.Now.ToLongTimeString(), GetType().Name, m_counterNotifyPersPlanChanged++));
    }

    private static int m_counterInternalFireNotification;
    private void InternalFireNotification(List<NotifyEventItem<TInfo>> pNotifyEventItem) {
        if (pNotifyEventItem == null || pNotifyEventItem.Count == 0)
            return;

        Debug.WriteLine(string.Format("{0} {1} Call {2} to InternalFireNotification",
            DateTime.Now.ToLongTimeString(), GetType().Name, m_counterInternalFireNotification++));

        // do not call the event handlers directly, because a transaction may be in progress and
        // we want the event handlers to see the end results of the modifications.
        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new HandleEntityChanged(NotifyEntityChanged), pNotifyEventItem.ToArray());
        Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new HandlePersPlanChanged(NotifyPersPlanChanged));
    }
}

每次通过服务修改实体的属性时,都会调用 NotifyEntityChanged

这里是大约 20 个 AbstractService 的实现。第一个是我遇到的问题,第二个有效。我将进一步描述问题所在。

public class ErgaenzungsfeldService : AbstractServiceBasic<ErgaenzungsfeldEntity, ErgaenzungsfeldData>, IErgaenzungsfeldService {

    [...]

    [EventPublication(Constants.TopicErgaenzungsfeldChanged, PublicationScope.Global)]
    public event EventHandler ErgaenzungsfeldChanged;

    private static int m_counterNotifyEntityChanged;
    protected override void NotifyEntityChanged(NotifyEventItem<IEntityInfoBasic>[] pNotifyEventItems) {
        Debug.WriteLine(string.Format("{0} {1} Call {2} to NotifyEntityChanged",
            DateTime.Now.ToLongTimeString(), GetType().Name, m_counterNotifyEntityChanged++));

        base.NotifyEntityChanged(pNotifyEventItems);
        if (ErgaenzungsfeldChanged != null) {
            ErgaenzungsfeldChanged(this, new NotifyEventArgs<IEntityInfoBasic>(pNotifyEventItems));
        }
    }
}

public class DienstleistenderService : AbstractAdaLanguageDependentServiceVersion<DienstleistenderEntity, DienstleistenderData>, IDienstleistenderService {

    [...]

    [EventPublication(Constants.TopicDienstleistenderChanged, PublicationScope.Global)]
    public event EventHandler DienstleistenderChanged;

    private static int m_counterNotifyEntityChanged;
    protected override void NotifyEntityChanged(NotifyEventItem<IEntityInfoVersion>[] pNotifyEventItem) {
        Debug.WriteLine(string.Format("{0} {1} Call {2} to NotifyEntityChanged",
            DateTime.Now.ToLongTimeString(), GetType().Name, m_counterNotifyEntityChanged++));
        if (DienstleistenderChanged != null) {
            DienstleistenderChanged(this, new NotifyEventArgs<IEntityInfoVersion>(pNotifyEventItem));
        }
    } 
 }

所有服务都像这样实例化:

WorkItem.RootWorkItem.Services.AddNew<ErgaenzungsfeldService, IErgaenzungsfeldService>();
WorkItem.RootWorkItem.Services.AddNew<DienstleistenderService, IDienstleistenderService>();

并使用以下两种方式之一进行访问:

WorkItem.Services.Get<IDienstleistenderService>()

[ServiceDependency]
public IErgaenzungsfeldService ErgaenzungsfeldService { get; set; }

问题: ErgaenzungsfeldService.NotifyEntityChanged 并不总是被调用。这种情况仅在导入数据时发生,而不是在“正常”使用应用程序时发生。通常我的意思是 ErgaenzungsfeldEntity 可以在应用程序中修改并保存。相同的机制正在使用并且有效。

一些观察:

  • 导入过程是单线程的。它不在 UI 线程中运行。
  • 当 ErgaenzungsfeldService.NotifyEntityChanged 未正确调用时,DienSTLeistenderService.NotifyEntityChanged 已正确调用
  • Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new HandleEntityChanged(NotifyEntityChanged), pNotifyEventItem.ToArray()) 替换为 NotifyEntityChanged(pNotifyEventItem.ToArray()) 或通过 System.Windows.Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new HandlePersPlanChanged(NotifyPersPlanChanged)); 正确调用 ErgaenzungsfeldService.NotifyEntityChanged 。当 UI 从另一个线程收到通知时,应用程序显然会在稍后抛出异常。
  • 如果通过 UI 保存 ErgaenzungsfeldEntity,则正确调用 ErgaenzungsfeldService.NotifyEntityChanged。

以下是出于调试目的而添加到不同方法中的消息的输出,这有助于我分析问题。

09:39:44 ErgaenzungsfeldService Call 0 to InternalFireNotification
09:39:44 ErgaenzungsfeldService Call 1 to InternalFireNotification
09:39:44 ErgaenzungsfeldService Call 2 to InternalFireNotification
09:39:44 ErgaenzungsfeldService Call 3 to InternalFireNotification
09:40:08 DienstleistenderService Call 0 to InternalFireNotification
09:40:08 DienstleistenderService Call 1 to InternalFireNotification
09:40:09 DienstleistenderService Call 2 to InternalFireNotification
[...]
09:40:14 DienstleistenderService Call 652 to InternalFireNotification
09:40:14 DienstleistenderService Call 653 to InternalFireNotification
09:40:14 DienstleistenderService Call 654 to InternalFireNotification
09:40:14 DienstleistenderService Call 655 to InternalFireNotification
09:40:14 AdaService Call 0 to InternalFireNotification
09:40:14 DienstleistenderService Call 656 to InternalFireNotification
09:40:14 DienstleistungService Call 0 to InternalFireNotification
09:40:14 AufbauorganisationService Call 0 to InternalFireNotification
09:40:14 AufbauorganisationService Call 1 to InternalFireNotification
09:40:14 AdaService Call 0 to NotifyPersPlanChanged
09:40:14 DienstleistenderService Call 0 to NotifyEntityChanged
09:40:16 DienstleistenderService Call 0 to NotifyPersPlanChanged
09:40:16 DienstleistungService Call 0 to NotifyPersPlanChanged
09:40:16 AufbauorganisationService Call 0 to NotifyPersPlanChanged
09:40:16 AufbauorganisationService Call 1 to NotifyPersPlanChanged

问题:什么可能导致通知未按描述执行?

[编辑]通过存储 DispatcherOperation,我获得了更多见解:

private List<DispatcherOperation> m_dispatcherOperationresults = new List<DispatcherOperation>();
private void InternalFireNotification(List<NotifyEventItem<TInfo>> pNotifyEventItem) {
   if (pNotifyEventItem == null || pNotifyEventItem.Count == 0)
      return;

   m_log.DebugFormat("InternalFireNotification called for {0}. TInfo type is {1}", GetType(), typeof(TInfo));

   // do not call the event handlers directly, because a transaction may be in progress and
   // we want the event handlers to see the end results of the modifications.
   DispatcherOperation result = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Send, new HandleEntityChanged(NotifyEntityChanged), pNotifyEventItem.ToArray());
   m_dispatcherOperationresults.Add(result);
   Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new HandlePersPlanChanged(NotifyPersPlanChanged));
}

这清楚地表明委托(delegate)执行被困在执行队列中。无论 DispatcherPriority 设置为 ApplicationIdle(一如既往)还是 Send(最高优先级),它们都会卡在 ErgaenzungsfeldService 中。

在此打印屏幕中,项目 0-3 已在导入期间从非 UI 线程触发并保持挂起状态,而项目 4 已从 GUI 线程触发(几分钟后)并执行 dispatcher priority results

问题:这些代表在为其他类别工作时仍等待某些类别,这可能是什么原因?

最佳答案

只是为了让我的评论变得“正式”。这可能是由于从另一个线程调用 Dispatcher.CurrentDispatcher 引起的。这可能会导致应用程序创建一个没有任何 UI 访问权限的新调度程序。

假设您从 UI 创建 ErgaenzungsfeldService 类,则可以创建一个值为 Dispatcher.CurrentDispatcher 的只读 Dispatcher 字段。这将为您提供 UI 调度程序。

关于c# - 在一种情况下 Dispatcher.BeginInvoke 未执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25199968/

相关文章:

c# - 将 ssh-keys 转换为 c# 格式以在 Windows (c#) 中加密并在 Linux (python/shell) 中解密

c# - SimpleMembershipProvider WebSecurity.InitializeDatabaseConnection 因空 sql 数据库而失败

c# - 使用 Azure DevOps 获取个人访问 token

c# - 用于在 ASP MVC 中同时发布 Action 和 Script 的按钮

c# - 我可以检测对象是否调用了 GC.SuppressFinalize 吗?

c# - 使用 VirtualAlloc 从字节数组运行程序?

android - 为什么kotlin协程可以在另一个线程上操作UI元素

c# - 如何在 C# 中组合两个类型为 "delegate"的 lambda

java代码执行在没有断点和正常运行的调试中产生不同的结果。 ExecutorService 坏了吗?

objective-c - 何时使用 enumerateObjectsUsingBlock 与 for