История проблемы
Это продолжение моего предыдущего вопроса
Как запустить поток, чтобы обновлять графический интерфейс?
но так какДжон пролил новый свет на проблему, мне пришлось бы полностью переписать оригинальный вопрос, который сделал бы эту тему нечитаемой.Итак, новый, очень специфический вопрос.
Задача
Две части:
- Обработка большого количества ресурсов ЦП в виде библиотеки (серверная часть)
- WPF GUI с привязкой данных, которая служит монитором для обработки (интерфейс)
Текущая ситуация - библиотека отправляет так много уведомлений об изменениях данных, что, несмотря на то, что она работает в своем собственном потоке, онаполностью блокирует механизм привязки данных WPF, и в результате не только мониторинг данных не работает (он не обновляется), но и весь GUI замораживается при обработке данных.
Цель - хорошо продуманный, отточенный способчтобы поддерживать графический интерфейс в актуальном состоянии - я не говорю, что он должен отображать данные немедленно (он может даже пропустить некоторые изменения), но он не может зависнуть при выполнении вычислений.
Пример
Этоупрощенный пример, но он показывает проблему.
Часть XAML:
<StackPanel Orientation="Vertical">
<Button Click="Button_Click">Start</Button>
<TextBlock Text="{Binding Path=Counter}"/>
</StackPanel>
Часть C # (пожалуйста, ОБРАТИТЕ ВНИМАНИЕ, это цельный код, но есть две его части):
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) - позволить потенциальному получателю "дышать" -- он работает, он минималистичен, хотя и уродлив, и он ВСЕГДА замедляет вычисления, даже если нет графического интерфейса пользователя
- на основе идеи Джона - отказаться от привязки данных ПОЛНОСТЬЮ (одного виджета с привязкой данных достаточно для заклинивания), и вместо этого время от времени проверяйте данные и обновляйте графический интерфейс вручную - ну, я не научился WPF просто отказаться от него сейчас; -)
- ТомасИдея - вставить прокси между библиотекой и внешним интерфейсом, который будет принимать все уведомления из библиотеки, и передавать некоторые из них в WPF, как, например, каждую секунду - недостатком является необходимость дублировать всеl объекты, которые отправляют уведомления
- на основе идеи Джона - передать диспетчер графического интерфейса в библиотеку и использовать его для отправки уведомлений - почему это уродливо?потому что вообще не может быть GUI
Мое текущее "решение" - добавление Sleep в основной цикл.Замедление незначительно, но его достаточно для обновления WPF (так что это даже лучше, чем спать перед каждым уведомлением).
Я весь в ушах для реальных решений, а не для некоторых хитростей.
Замечания
Замечание об отказе от привязки данных - для меня дизайн его нарушен, в WPF у вас есть один канал связи, вы не можете привязать напрямую к источникуизменения.Привязка данных фильтрует источник по имени (строка!).Это требует некоторых вычислений, даже если вы используете некоторую умную структуру, чтобы сохранить все строки.
Редактировать: Замечание по абстракциям - назовите меня старым таймером, но я начал изучать компьютер, убежденный, что компьютерыдолжен помогать людям.Повторяющиеся задачи - это область компьютеров, а не людей.Неважно, как вы это называете - MVVM, абстракции, интерфейс, одиночное наследование, если вы пишете один и тот же код снова и снова, и у вас нет способа автоматизировать то, что вы делаете, вы используете сломанный инструмент.Так, например, лямбды - это здорово (меньше работы для меня), но одиночное наследование - нет (больше работы для меня), связывание данных (как идея) - это здорово (меньше работы), но нужен прокси-слой для EVERY библиотека, с которой я связываюсь, - неработающая идея, потому что она требует много работы.