Пользовательский интерфейс и многопоточность.
От: Аноним  
Дата: 20.10.05 13:58
Оценка:
Добрый день.

При разработке пользовательского интерфейса клиентской части к серверу собственной разработки (на основе блокирующих синхронных winsock + delphi) мы столкнулись с рядом проблем.

Главным требование к клиенту является — запросы должны выполняться асинхронно.
Запросы бывают двух видов:
1. Явно инициированные пользователем (например, нажатие на кнопку)
2. Запросы по таймеру.

В первом случае под асинхронностью главным образом понимается следующее:
1. Пользователь нажал кнопку.
2. Запрос обрабатывается.
3. Но главная форма не "висит", т.е. как минимум перерисовывается.

В этом случае решается все просто (пока сделано именно так) — на время выполнения запроса показывается диалоговое окно (типа — подождите, идет запрос...). Сообщения windows обрабатываются, т.е. форма перерисовывается. При получении сообщения из потока, выполняющего запрос, диалоговое окно закрывается и происходит обновление клиентских структур данных.

В запросах второго вида (по таймеру) все сложнее. Данные запросы должны выполняться невидимо пользователю (т.е. блокировка gui на время выполнения недопустима). При наступлении определенного события (например, на сервере обновились данные и их нужно обновить на клиенте), поток, выполняющий запрос, посылает сообщение главному потоку (тому, который ответственнен за gui). Тут есть некотолрые проблемы. Например, пользователь пользуется клиентскими структурами данных (например, происходит какой-то долгий математический расчет). Поэтому, изменение стукртур данных недопустимо. Приходится откладывать обновление до момента, когда пользователь перестает пользоваться данными.

При всем этом программировании, есть ощущение, что ты решаешь проблемы, которые уже кем-то успешно решены и осмысленны.

Поэтому вопрос: есть ли в природе некая литература по созданию многопоточных пользовательских интерфейсов? Например, какие-то паттерны (типа GoF) или еще что? И в частности по реализации на клиенте асинхронных запросов?
Re: Пользовательский интерфейс и многопоточность.
От: c-smile Канада http://terrainformatica.com
Дата: 20.10.05 20:38
Оценка: 2 (2) +1
Здравствуйте, Аноним, Вы писали:

Я не понял проблемы...


void MyThreadFunction()
{
Делаем чего...
....
Сделали:
::PostMessage(m_hWnd,WM_НАШАРОДНАЯ_МЕССАГА,....);
}

CreateThread(&MyThreadFunction)

какие тут еще дизайн патерны нужны?
Re[2]: Пользовательский интерфейс и многопоточность.
От: Суслик Россия http://www.vkkb.ru
Дата: 21.10.05 08:52
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>какие тут еще дизайн патерны нужны?


Согласен, что исходный вопрос несколько неконкретен.

В исходном впоросе есть такой пример — gui не готово обработать сообщение, посланное потоком, посылающим запрос. Пример был приведен.

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

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

Если вспомнить, тех же GoF, то у них есть масса примеров по gui. Фактически вся книга этому посвещена. Тот же Влессидес пишет, что на стадии разработки книги они выкинули из рассмотрения многопточные паттерны. Очевидно, что если бы они их включили, то скорее всего они были бы именно по gui, как и вся книга.

Вот я испрашиваю, знает ли кто-нибудь, примеры многопточных паттернов, относящихся к gui.

Надеюсь конкретизировал вопрос
Re[3]: Пользовательский интерфейс и многопоточность.
От: vladserge Россия  
Дата: 21.10.05 15:50
Оценка: +1 -1 :))
Здравствуйте, Суслик, Вы писали:

С>Вот я испрашиваю, знает ли кто-нибудь, примеры многопточных паттернов, относящихся к gui.


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

С>Надеюсь конкретизировал вопрос


Есть надежда, что паттерны заменяют необходимость думать головой?
С Уважением Сергей Чикирев
Re[3]: Пользовательский интерфейс и многопоточность.
От: c-smile Канада http://terrainformatica.com
Дата: 21.10.05 19:40
Оценка:
Здравствуйте, Суслик, Вы писали:

С>Согласен, что исходный вопрос несколько неконкретен.


С>В исходном впоросе есть такой пример — gui не готово обработать сообщение, посланное потоком, посылающим запрос. Пример был приведен.


Что значит не "gui не готово"?

Для справки ::PostMessage кладет сообщение во входную очередь приложения.
Выборка оного осуществялется именно в тот момент когда "GUI готово" если я правильно понимаю
подразумеваемый смысл.
Re: Пользовательский интерфейс и многопоточность.
От: mishaa Россия http://kmmbvnr.livejournal.com
Дата: 22.10.05 05:29
Оценка:
Здравствуйте, Аноним, Вы писали:

А>В первом случае под асинхронностью главным образом понимается следующее:

А>1. Пользователь нажал кнопку.
А>2. Запрос обрабатывается.
А>3. Но главная форма не "висит", т.е. как минимум перерисовывается.

А>Поэтому вопрос: есть ли в природе некая литература по созданию многопоточных пользовательских интерфейсов? Например, какие-то паттерны (типа GoF) или еще что? И в частности по реализации на клиенте асинхронных запросов?


Например в SWT все гуи однопоточное, когда тебе надо что-то сделать с гуевиной из другого потока, пишешь примерно так


Display.run().syncExec(new Runnable()
 {
    public void run()
    {
       //do something
    }
 });
ну или
Display.run().аsyncExec() ....


imho достаточно удобный паттерн.
-- Главное про деструктор копирования не забыть --
Re[4]: Пользовательский интерфейс и многопоточность.
От: Суслик Россия http://www.vkkb.ru
Дата: 23.10.05 07:41
Оценка:
Здравствуйте, vladserge, Вы писали:

V>Здравствуйте, Суслик, Вы писали:


С>>Вот я испрашиваю, знает ли кто-нибудь, примеры многопточных паттернов, относящихся к gui.


V>Слушай, спустись на землю, какие паттерны, чего все с ним носятся как очумелые?

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

С>>Надеюсь конкретизировал вопрос


V>Есть надежда, что паттерны заменяют необходимость думать головой?


Грубовато, уж извини.
Re[4]: Пользовательский интерфейс и многопоточность.
От: Суслик Россия http://www.vkkb.ru
Дата: 23.10.05 08:04
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Что значит не "gui не готово"?


CS>Для справки ::PostMessage кладет сообщение во входную очередь приложения.

CS>Выборка оного осуществялется именно в тот момент когда "GUI готово" если я правильно понимаю
CS>подразумеваемый смысл.

Добраый день. Спасибо за внимание к вопросу.

Не знаю, на чем ты пишешь (вроде на чем-то си-подобном).

Я же на delphi. Смотри, в дельфи организовано все так (привожу описание, дабы был понятен смысл вопроса и возможное направление будущих советов).

Факты:
1. GUI в дельфи однопоточное. Конечно, можно что-то такое сделать в многопоточном режиме, но последсвтия в общем случае непредсказуемы.
2. В стандартном классе TThread (обертка для использования потоков) есть функция Synchronize для синхронизации вызова метода в контексте главного потока.
3. Данная функция является по своей сути синхронной (т.е. вызывал ее из потока и ждешь пока функция не будет выполнена в контексте главного потока).
4. Фукнциональность Synchronize реализована на сообщениях windows (в разных версиях сообщения разные — раньше было send, теперь post с ожидаением результата, через объекты синхронизации).
5. В дельфи есть метод ProcessMessages. Вызов данного метода в контексте главного потока приводит к обработке очереди сообщения (в том числе и сообщений от метода Synchronize).


Теперь ситуация:
1. Есть клиентская структура данных (как было сказано в исходном вопросе — точная копия аналогичной стурктуры на сервере).
2. Стукртура небольшая, поэтому вся содержиться в памяти. Но при этом достаточно сложная (математическая экономическая можель).
3. Клиентская часть в ФОНОВОМ режиме переодически проводит синронизация с сервером — т.е. выкачивает с сервера некий (небольшой) объем данных, достаточный для репликации.
4. Т.к. данная операция проходит в отдельном потоке, то ентот поток должен иметь возможность известить главный о том, что есть данные для репликации.
5. Теперь самое главное — о неготовности GUI. Представим ситуацию, когда пользователь запустил некую операцию, использующую данные. Например, решил выгрузить, например в excel, определенный кусок данных. Вообще говоря, данная операция может быть досточно долгой. Ясно (из условий задачи), что данная операция должна быть реализована в уровне изоляции serializable, т.е. недопустим случай когда начинали выгружать одни данные, потом была репликация и закончили выгружать уже другие (измененные) данные. Но! т.к. операция выгрузки долгая, то главный поток (тот который, ответственнен за gui), не должен "умирать". Например, он должен перерисовывать ProgressBar. Я для того, чтобы он не умирал нужно переодически (в момент выгрузки данных) вызывать вышеуказанный ProcessMessages, который, как также было сказано выше, обработает в том числе и сообщения от фонового потока.

Проблемы:
1. Получается, что есть моменты, когда сообщение от фонового потока пришло, а обработать результат нельзя.

Решения:
1. Можно откладывать обработку запроса на репликацию, введя некий счетчик блокировки данных. Т.е. пойти путем блокировок.
2. Можно приостанавливать фоновый поток в момент долгих операций.
3. Можно пойти путем версионности — т.е. в момент начала долгих операций в клиенте копировать нужны данные.

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

ЗЫ. Замечу, что совет вынести выгрузку в файл в отдельный поток и не вызывать ProcessMessages не решает проблемы — остануться случаи, когда главный поток не готов провести обновления.
Re[2]: Пользовательский интерфейс и многопоточность.
От: Суслик Россия http://www.vkkb.ru
Дата: 23.10.05 08:11
Оценка:
Здравствуйте, mishaa, Вы писали:

M>imho достаточно удобный паттерн.


Да в дельфи такой же есть. Но проблемы он не решает. Я ответил c-smile выше...
Re[5]: Пользовательский интерфейс и многопоточность.
От: c-smile Канада http://terrainformatica.com
Дата: 23.10.05 16:53
Оценка:
Здравствуйте, Суслик, Вы писали:

CS>>Что значит не "gui не готово"?


Дельфи не знаю. Но у меня в HTMLayout например похожая ситуация.
Асинхронная загрузка картинок.

Сам HTMLayout работает в основном GUI потоке.
Есть асинхроный resource pump (thread pool + in/out queues)

resource pump качает (например картинку) и выкладывает её в
out queue — готовые картинки — ready to use и делает ::PostMessage(ЗАБИРАЙ!)
GUI потоку. т.е. я уже вижу картинку целиком. Я не стал делать incremental image rendering —
это по большому счету не актуально сейчас
.
Все классически просто.
Re[5]: Пользовательский интерфейс и многопоточность.
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.10.05 05:47
Оценка: 2 (1)
Здравствуйте, Суслик, Вы писали:

С>Теперь ситуация:

С>1. Есть клиентская структура данных (как было сказано в исходном вопросе — точная копия аналогичной стурктуры на сервере).
С>2. Стукртура небольшая, поэтому вся содержиться в памяти. Но при этом достаточно сложная (математическая экономическая можель).
С>3. Клиентская часть в ФОНОВОМ режиме переодически проводит синронизация с сервером — т.е. выкачивает с сервера некий (небольшой) объем данных, достаточный для репликации.
С>4. Т.к. данная операция проходит в отдельном потоке, то ентот поток должен иметь возможность известить главный о том, что есть данные для репликации.
С>5. Теперь самое главное — о неготовности GUI. Представим ситуацию, когда пользователь запустил некую операцию, использующую данные. Например, решил выгрузить, например в excel, определенный кусок данных. Вообще говоря, данная операция может быть досточно долгой. Ясно (из условий задачи), что данная операция должна быть реализована в уровне изоляции serializable, т.е. недопустим случай когда начинали выгружать одни данные, потом была репликация и закончили выгружать уже другие (измененные) данные. Но! т.к. операция выгрузки долгая, то главный поток (тот который, ответственнен за gui), не должен "умирать". Например, он должен перерисовывать ProgressBar. Я для того, чтобы он не умирал нужно переодически (в момент выгрузки данных) вызывать вышеуказанный ProcessMessages, который, как также было сказано выше, обработает в том числе и сообщения от фонового потока.

С>Проблемы:

С>1. Получается, что есть моменты, когда сообщение от фонового потока пришло, а обработать результат нельзя.

С>Решения:

С>1. Можно откладывать обработку запроса на репликацию, введя некий счетчик блокировки данных. Т.е. пойти путем блокировок.
С>2. Можно приостанавливать фоновый поток в момент долгих операций.
С>3. Можно пойти путем версионности — т.е. в момент начала долгих операций в клиенте копировать нужны данные.

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

Отлично сформулированный вопрос. Пять с плюсом!
Все проблемы происходят от смешивания нескольких моделей синхронизации.
Давай посмотрим на это под вот таким углом:
1. Есть некоторая модель.
2. Есть две активности:
2.1. Обновление модели (твой фоновый репликатор)
2.2. Чтение модели (твой экспортер в эксель)

Что мы хотим? Чтобы все активности работали с согласованными данными.
Наивный подход — использование сочетания Synchronize с ProcessMessages — чреват тяжким геморроем. И это оправдано. В мало-мальски сложных случаях имеет смысл оставить эти средства только для управления GUI. В данном случае я рекомендую вынести обе активности в фоновые потоки.

Альтернатива — делать все честно.
Есть, как обычно, два варианта:
1. Блокировки. Например, ExclusiveWriteMultipleRead. Поток обновления захватывает эксклюзивную блокировку модели, обновляет ее, затем отпускает блокировку и постит в GUI поток сообщение "модель изменилась" чтобы обеспечить перерисовку.
Поток экспорта захватывает рид-блокировку на данные, и периодически обновляет состояние прогресса используя Synchronize (отдельных сообщений типа Модель Изменилась не надо — мы же только читаем).
Если оказалось, что обновление началось в момент активного экспорта, ему придется дождаться окончания. Если мы пытаемся начать экспорт в момент активного обновления, ему тоже придется подождать. Кстати, ждать придется и GUI, как только он попробует перерисоваться, обратившись к модели.
В принципе — нормальный вариант. Недорогой — достаточно расставить обращения к синхронизационным объектам в нужные места. Недостаток — замораживание GUI в момент фонового обновления. Если нестрашно получать несогласованные данные в процессе обновления, то можно неблокировать GUI. Но экспорт по любому зависнет на 0% до тех пор, пока обновление не закончится.

2. Версионность.
Поток обновления (ну или любая другая активность, которая хочет менять данные) клонирует всю модель, и выполняет обновления на клоне. Поток экспорта (или любая другая активность, которая хочет читать согласованную модель) блокирует версию на время своей работы. Пишушая активность в конце своей деятельности выполняет подмену модели: текущая модель заменяется клоном, а старая уничтожается. В этот момент может потребоваться дождаться читателей, которые все еще пользуют старую модель.
Преимуществом этого варианта является более высокая "одноврменность": экспорт и обновление перестают мешать друг другу.
Но есть некоторые трудности:
— необходимость поддерживать клонирование модели. Может оказаться слишком дорогим, когда модель велика, а обновления незначительны. Реализация своей иерархи более гранулярных версионных блокировок — та еще песня. Приложение может усложниться раза в четыре, по сравнению с "чистой" версией.
— проблемы при наличии более чем 1й модифицирующей активности. Придется как-то разводить разных "модификаторов", чтобы избежать потери апдейтов.
— необходимость реализовывать свою механику коммита, т.е. подмены моделей.

Так что я рекомендую начать с варианта 1, а уже потом, если появится реальная необходимость, реализовать что-то более сложное.
1.1.4 stable rev. 510
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: Пользовательский интерфейс и многопоточность.
От: Суслик Россия http://www.vkkb.ru
Дата: 24.10.05 08:03
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Так что я рекомендую начать с варианта 1, а уже потом, если появится реальная необходимость, реализовать что-то более сложное.


Спасибо.
На этом варианте пока и остановимся.
Re[5]: Пользовательский интерфейс и многопоточность.
От: ArtemGorikov Австралия жж
Дата: 29.10.05 13:39
Оценка:
Здравствуйте, Суслик, Вы писали:

С>Здравствуйте, vladserge, Вы писали:


V>>Слушай, спустись на землю, какие паттерны, чего все с ним носятся как очумелые?

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

С>>>Надеюсь конкретизировал вопрос


V>>Есть надежда, что паттерны заменяют необходимость думать головой?


С>Грубовато, уж извини.


Нужно было не паттерн спрашивать, а "киньте ссылку на компонент который все сделает и нитку интерфейса не залочит", а Вы все "паттерны, GoF"
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.