Индикатор занятости для нескольких асинхронных вызовов

У меня есть свойство Busy, для которого установлено значение true до того, как будет сделан асинхронный вызов, а затем установлено значение false по завершении. Теперь у меня есть 2 асинхронных вызова, как мне справиться с этой логикой? Нужно ли мне блокировать переменные или какие-то другие параллельные проблемы, на которые мне нужно обратить внимание?

private bool _busy;

public bool Busy
{
    get { return _busy; }
    set
    {
        bool changed = value != _busy;
        _busy = value;
        if (changed) RaisePropertyChanged("Busy");
    }
}

private void loadUser(int userId)
{
        Busy = true;
        api.GetUser(userId, CancellationToken.None).ContinueWith(t =>
                                      Deployment.Current.Dispatcher.BeginInvoke(() =>
                                      {
                                          Busy = false;
                                      }));

}

private void loadOtherData(int dataId)
{
        Busy = true;
        api.GetData(dataId, CancellationToken.None).ContinueWith(t =>
                                      Deployment.Current.Dispatcher.BeginInvoke(() =>
                                      {
                                          Busy = false;
                                      }));

}

Я знаю, что эта логика ошибочна из-за того, что для свойства Busy установлено значение false в первом методе, завершающем выполнение. У меня есть идея использовать еще 2 поля; isUserLoading и isOtherDataLoading и убедитесь, что оба имеют значение false, прежде чем устанавливать значение false для Busy.

Я хотел бы знать, есть ли лучший способ сделать это.


person Shawn Mclean    schedule 07.05.2013    source источник
comment
Я использовал числовой счетчик (который ваши асинхронные вызовы будут увеличивать/уменьшать) с таким свойством, как bool Busy { get { return _count != 0; } }. Мне интересно посмотреть, что другие рекомендуют.   -  person mztan    schedule 07.05.2013


Ответы (2)


Если у вас есть два логических значения, _isUserLoading и _isOtherDataLoading, которые вы обновляете в своих методах загрузки, вы можете просто изменить Busy на это:

public bool busy
{
    get
    {
         return _isUserLoading || _isOtherDataLoading;
    }
}

Другая версия, включающая вызов RaisePropertyChanged, может работать так:

public bool busy
{
    get
    {
         return _isUserLoading || _isOtherDataLoading;
    }
}

public bool IsUserLoading
{
    get
    {
         return _isUserLoading;
    }
    set
    {
       bool busy = Busy;
       _isUserLoading = value;
       if (busy != Busy) RaisePropertyChanged("Busy");
    }
}

И конечно аналогичное свойство для IsOtherDataLoading.

person joshuahealy    schedule 07.05.2013
comment
Должен ли я беспокоиться о блокировках на объекте Busy? - person Shawn Mclean; 07.05.2013
comment
Как бы я также справился с этим для свойства уведомления? я отредактировал свой вопрос - person Shawn Mclean; 07.05.2013
comment
См. мое редактирование выше, чтобы узнать, как вы можете включить вызов RaisePropertyChanged. - person joshuahealy; 07.05.2013

В идеале вы хотите предоставить наблюдаемое свойство/событие из API, указывающее, когда оно занято. Предполагая, что вы не можете вносить какие-либо изменения, я предлагаю вам использовать более общий подход. Что-то типа

class CountedContext {
    int workersCount  = 0;
    Action<bool> notifier;  

    public CountedContext(Action<bool> notifier) { this.notifier = notifier; }

    public Task<TResult> Execte<TResult>(Func<Task<TResult>> func)
    {   
        lock(worksersCount) 
        { 
            workersCount++; 

            if (workdersCount == 1)
                notifier(true);
        }

        var result = func();

        result.ContinueWith(_ => 
        {
            lock (worksersCount)
            {
                workersCount--;

                if (worksersCount == 0){
                    notifier(false);
                }
            }
        });

        return result;
    }
}

В своем основном классе вы можете использовать:

// constructor:
countedContext = new CountedContext(state => 
{
    ...BeginInvoke(() =>
    {
        Busy = state;
    });
});

...

// loadData
countedContext.Execute(() => api.GetData());
person Polity    schedule 07.05.2013
comment
Вы можете использовать аналогичную технику, но более встроенную в API. - person Polity; 07.05.2013