История проблемы
Это продолжение моего предыдущего вопроса
Как запустить поток для обновления GUI?
но поскольку Джон пролил новый свет на проблему, мне пришлось бы полностью переписать оригинальный вопрос, который сделает эту тему нечитаемой. Итак, новый, очень конкретный вопрос.
Проблема
Две части:
- Процессорная голодная тяжелая обработка как библиотека (back-end)
- WPF GUI с привязкой данных, которая служит в качестве монитора для обработки (front-end)
Текущая ситуация - библиотека отправляет так много уведомлений об изменениях данных, которые, несмотря на то, что она работает в пределах своего потока, полностью замалчивает механизм привязки данных WPF, и в результате не только мониторинг данных не работает (он не обновляется), но и весь Графический интерфейс заморожен во время обработки данных.
Цель - продуманный, отполированный способ постоянно обновлять графический интерфейс - я не говорю, что он должен немедленно отображать данные (он может пропустить некоторые изменения даже), но он не может заморозить при выполнении вычислений.
Пример
Это упрощенный пример, но он показывает проблему.
Часть XAML:
<StackPanel Orientation="Vertical">
<Button Click="Button_Click">Start</Button>
<TextBlock Text="{Binding Path=Counter}"/>
</StackPanel>
Часть С# (пожалуйста, обратите внимание, что это код одной части, но есть два его раздела):
public partial class MainWindow : Window,INotifyPropertyChanged
{
// GUI part
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var thread = new Thread(doProcessing);
thread.IsBackground = true;
thread.Start();
}
// this is non-GUI part -- do not mess with GUI here
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string property_name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property_name));
}
long counter;
public long Counter
{
get { return counter; }
set
{
if (counter != value)
{
counter = value;
OnPropertyChanged("Counter");
}
}
}
void doProcessing()
{
var tmp = 10000.0;
for (Counter = 0; Counter < 10000000; ++Counter)
{
if (Counter % 2 == 0)
tmp = Math.Sqrt(tmp);
else
tmp = Math.Pow(tmp, 2.0);
}
}
}
Известные обходные пути
(Пожалуйста, не отправляйте их в качестве ответов)
Я отсортировал список в соответствии с тем, насколько мне нравится обходной путь, т.е. сколько требуется работы, его ограничений и т.д.
- Это мое, это уродливо, но простота его убивает - перед отправкой уведомления замораживать нить - Thread.Sleep(1) - чтобы потенциальный приемник "дышал" - он работает, он минималистичен, он уродлив, и он ВСЕГДА замедляет вычисление, даже если графический интерфейс отсутствует
- на основе идеи Джона - отказаться от привязки данных ПОЛНОСТЬЮ (один виджет с привязкой данных достаточен для заклинивания), а вместо этого время от времени проверять данные и обновлять GUI вручную - Я не учил WPF просто отказаться от него сейчас; -)
- Идея Thomas - вставьте прокси-сервер между библиотекой и интерфейсом, который будет получать все уведомления из библиотеки и передать некоторые из них в WPF, например, каждую секунду - недостатком является то, что вы должны дублировать все объекты, отправляющие уведомления
- на основе идеи Jon - передать диспетчер графического интерфейса в библиотеку и использовать ее для отправки уведомлений - почему это уродливо? потому что вообще не может быть GUI
Мое текущее "решение" добавляет Sleep в основной цикл. Замедление незначительно, но достаточно, чтобы WPF обновлялся (так что даже лучше, чем спать перед каждым уведомлением).
Я все уши для реальных решений, а не некоторые трюки.
Примечания
Замечание об отказе от привязки данных - для меня дизайн его сломан, в WPF у вас есть единственный канал связи, вы не можете напрямую привязываться к источнику изменения. Связывание данных фильтрует источник на основе имени (строки!). Это требует некоторых вычислений, даже если вы используете какую-то умную структуру, чтобы сохранить все строки.
Изменить: Замечание об абстракциях - назовите мне старый таймер, но я начал изучать компьютер, убежденный, что компьютеры должны помогать людям. Повторяющиеся задачи - это область компьютеров, а не людей. Независимо от того, как вы это называете - MVVM, абстракции, интерфейс, одно наследование, если вы пишете один и тот же код, снова и снова, и у вас нет способа автоматизировать то, что вы делаете, вы используете сломанный инструмент. Так, например, lambdas отличные (меньше работы для меня), но одно наследование не является (больше работы для меня), привязка данных (как идея) велика (меньше работы), а необходимость прокси-уровня для КАЖДЫЙ библиотека, которую я связываю, - это сломанная идея, потому что она требует много работы.