Будет ли вызов, сделанный из Dispatcher.BeginInvoke (), всегда начинаться после завершения текущего метода? - PullRequest
0 голосов
/ 16 февраля 2011

У меня есть метод расширения, который я использую для запуска уведомлений PropertyChanged, который выглядит примерно так:

    public static TRet RaiseIfChanged<TObj, TRet>(this TObj target, Expression<Func<TObj, TRet>> property, TRet newValue)
        where TObj : AlantaViewModelBase
    {
        var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;
        var propertyValue = propertyInfo.GetValue(target, null);

        if (!EqualityComparer<TRet>.Default.Equals((TRet)propertyValue, (TRet)newValue))
        {
            // Marshal this to the dispatcher so that the RaisePropertyChanged() notification happens after 
            // the value is actually set.
            Deployment.Current.Dispatcher.BeginInvoke(() => target.RaisePropertyChanged(propertyInfo.Name));
        }
        return newValue;
    }

Он используется так:

    private bool isBusy;
    public bool IsBusy
    {
        get { return isBusy; }
        set { isBusy = this.RaiseIfChanged(p => p.IsBusy, value); }
    }

Мой вопросо Dispatcher.BeginInvoke(), который я использую для фактического вызова target.RaisePropertyChanged().Учитывая, что свойство фактически не изменится, пока не завершится метод расширения, я должен быть уверен, что событие на самом деле не будет инициировано до тех пор, пока не будет установлено значение свойства.Могу ли я быть уверен, что так будет всегда?Или есть случаи, когда событие будет запущено за до того, как свойство будет изменено?Или есть лучший способ справиться с этим?

Ответы [ 4 ]

3 голосов
/ 16 февраля 2011

У вас нет такой гарантии. Точное время выполнения метода RaisePropertyChanged () сильно зависит от состояния потока пользовательского интерфейса. И если он просто оказывается готовым просмотреть очередь вызовов для целей вызова, выполняемых в тот момент, когда ваш рабочий поток вызывает RaiseIfChanged, и рабочий поток теряет квант процессора, тогда да, этот вызов будет сделан до того, как рабочий может вернуть и установить значение свойства. Маловероятно, но путь добрых намерений усеян дорожным убийством, подобным этому.

Вы должны присвоить свойству перед возбуждение события. Un-dry test-and-set довольно прост. Оставайтесь сухими, но уродливыми, используя PropertyInfo.SetValue () или передавая вспомогательную переменную по ссылке. Лично я бы не стал использовать рефлексию для чтения значения свойства на три порядка медленнее, чем просто кодировать его напрямую.

2 голосов
/ 16 февраля 2011

Ваш код не гарантирует выполнение заказа. То есть событие может быть передано до того, как свойство будет установлено. Например, на однопроцессорной машине не удивительно, что диспетчер просто выполняет код синхронно. В этом случае событие будет передано перед возвратом из метода.

На многоядерном или многопроцессорном компьютере все ставки выключены. Я подозреваю, что вы обнаружите, что иногда событие сигнализируется до изменения свойства, а иногда - нет.

Безопасный способ сделать это:

set 
{
    var oldValue = isBusy;
    isBusy = value;
    this.RaiseIfChanged(oldValue, value); 
}

Конечно, это требует изменений в вашем методе расширения. , .

0 голосов
/ 16 февраля 2011

Нет предположения о том, когда оно будет выполнено.Он будет выполняться сразу (в случае, если вы работаете из потока пользовательского интерфейса) или в любой момент в будущем.

0 голосов
/ 16 февраля 2011

Dispatcher.BeginInvoke () планирует новый поток для запуска лямбды.Этот поток будет запущен, как только будет процессорное время.Я предполагаю, что метод RaisePropertyChanged () вручную запускает событие, которое в .NET обычно реализуется с синхронным MulticastDelegate.Если это все так, обработчик событий МОЖЕТ работать до того, как фактическое свойство будет изменено;зависит от того, будет ли это зависеть от аппаратных деталей, которые вы не можете контролировать, но если не считать, вы не можете гарантировать это.

Способ, которым вы должны сделать что-то подобное, - сохранить исходное значение, выполнить назначениеТО поднимите событие, если действительно произошло изменение в значении.ПОЦЕЛУЙ: вы можете переопределить RaiseIfChanged, чтобы взять старое значение вместо проекции, или вы можете выполнить присвоение в RaiseIfChanged до запуска события.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...