Как описывать действия над gui? (ED, cps, async, yield) [re: Я раз]
Ну типа например GUI взаимодействует с бизнес-логикой через IPC. Если завтра захочется веб-интерфейс или там мобильный клиент, то надо переписать только GUI.Мы и говорим о примере, когда GUI взаимодействует с бизнес логикой через IPC.
Никаких потоков, GUI работает по принципу отправил запрос - подождал - получил ответ.Пока GUI ждёт ответ, он не должен блокироваться; и ещё было бы неплохо нарисовать какой-нибудь progress bar.
Пока GUI ждёт ответ, он не должен блокироваться; и ещё было бы неплохо нарисовать какой-нибудь progress bar.ну и какая проблема, GUI же делают на основе message loop
пришёл ответ от базы - сообщение доходит обычным образом, а прогресс бар рисуется по сообщениям от таймера
ну и какая проблема, GUI же делают на основе message loopПроблем несколько. Например, мы обсуждали возможность небольшой обработки данных после ответа от сервера, которая занимает несколько секунд. Да и получить ответ от сервера сразу в message loop не всегда так просто, да и не всегда возможно.
пришёл ответ от базы - сообщение доходит обычным образом, а прогресс бар рисуется по сообщениям от таймера
Да и получить ответ от базы в message loop не всегда так просто, да и не всегда возможно.возможно, для этого достаточно однажды написать библиотечную функцию, которая перекидывает ответ в виде gui-сообщения, аля:
void DbExecute(this Control control, string query, Action onexecuteaction)
{
AsyncDbExecute(query, =>control.BeginInvoke(onexecuteaction;
}
ps
код действий правда не очень красивый получается, ломается блочная логика (не получается нативно использовать деструкторы, try/catch, using, for и т.д.)
но это решается, если в языке есть cps
возможно, для этого достаточно однажды написать библиотечную функцию, которая перекидывает ответ в виде gui-сообщения, аля:Возможно, ещё для этого подходит паттерн State, и я подобное решение опробовал на практике. В итоге код было очень сложно поддерживать. На C# я подхватил идею Рихтера и стал использовать yiled.
Ну типа например GUI взаимодействует с бизнес-логикой через IPC.минусы:
1. размазывается логика по куче слоев (любую долгую хреньку необходимо делать в виде отдельного агента)
2. необходимо постоянно держать в голове - это быстрое действие, или это долгое действие? причем, напрямую, по коду это не видно
3. вместо нативных конструкций (for, using, try, вызов функции и т.д.) приходится городить их аналоги
4. необходимо писать доп. код, по формированию и обработке сообщений
GUI взаимодействует с бизнес-логикой через IPC. Если завтра захочется веб-интерфейс или там мобильный клиент, то надо переписать только GUI.и это, кстати, приводит к утверждению, которое противоречит идеям бизнеса(рубль завтра намного дешевле, чем рубль сегодня, как минимум на двадцать годовых): давайте напишем сегодня в несколько раз сложнее(давайте сегодня потратим сегодня в несколько раз больше денег и это может быть поможет потратить денег меньше когда-нибудь.
хочется-то, при той же сравнимой сложности(а лучше еще и сильно упростить) сделать так, чтобы легко было переделывать.
поэтому все делают GUI, которое подвисает - так дешевле всего
минусы:большинство из этих пунктов уходят, если предложение -а:
1. размазывается логика по куче слоев (любую долгую хреньку необходимо делать в виде отдельного агента)
2. необходимо постоянно держать в голове - это быстрое действие, или это долгое действие? причем, напрямую, по коду это не видно
3. вместо нативных конструкций (for, using, try, вызов функции и т.д.) приходится городить их аналоги
4. необходимо писать доп. код, по формированию и обработке сообщений
GUI работает по принципу отправил запрос - подождал - получил ответ.
оформить вот в такую конструкцию (реализация через Castle.DynamicProxy):
class Program
{
static void Main
{
Execute(
facade => facade.Method1("test1", 3
result => Console.WriteLine(result
exception => Console.WriteLine(exception;
}
static void Execute<TResult>(
Func<IFacade, TResult> func,
Action<TResult> onSuccessAction,
Action<Exception> onErrorAction)
{
throw new NotImplementedException;
}
}
public interface IFacade
{
string Method1(string arg1, int arg2);
}
public class Facade: IFacade
{
public string Method1(string arg1, int arg2)
{
//операция выполняется не в UI-ом потоке
throw new NotImplementedException;
}
}
т.е. под ReSharper-ом такой код сопровождается без какого-либо заметного оверхеда
static void Execute<TResult>(еще удобно дополнить этот метод перегруженным
Func<IFacade, TResult> func,
Action<TResult> onSuccessAction,
Action<Exception> onErrorAction)
{
throw new NotImplementedException;
}
static void Execute<TResult, TContinue>(
Func<IFacade, TResult> func,
Func<TResult, TContinue> onSuccessFunc,
Func<Exception, TContinue> onErrorFunc,
Action<TContinue> onContinueAction /*выполняется после onSuccessFunc или onErrorFunc*/)
{
throw new NotImplementedException;
}
Оба метода легко выражаются друг через друга.
большинство из этих пунктов уходят, если предложение -а оформить вот в такую конструкцию (реализация через Castle.DynamicProxy):обоснуй, пожалуйста
это CPS что ли?
а в каком месте там получается другой тред?
Другой тред там не написал, он зашит в реализацию Execute. Идяе -а в том, что единственное место, где явно присутствуют треды в коде - это реализация Execute.
это CPS что ли?насколько я понимаю, да
а в каком месте там получается другой тред?
С помощью Castle.DynamicProxy можно сделать динамическую реализацию (proxy) интерфейса IFacade. Этот прокси перенаправляет вызовы всех методов в одну точку:
namespace Castle.DynamicProxy
{
public interface IInterceptor
{
void Intercept(IInvocation invocation);
}
}
Мы подсовываем свою реализацию IInterceptor, в которой и происходит переброска в другой поток и обратно.
насколько я понимаю, даесли это и cps, то это очень недо-недо cps, имеющий все те недостатки, которые я перечислил.
как, например, в таком коде выглядит обработка ошибок? и насколько она отличается от нативной, через обработку исключений?
да и он не недостаток, если приложение уже event-driven
как, например, в таком коде выглядит обработка ошибок? и насколько она отличается от нативной, через обработку исключений?Такими темпами вы скоро веб-интерфейсы изобретете!
как, например, в таком коде выглядит обработка ошибок? и насколько она отличается от нативной, через обработку исключений?обработка ошибок в onErrorAction.
Тебе хочется оборачивать в один try куски из UI кода и куски не UI кода? А нафига это надо?
Такими темпами вы скоро веб-интерфейсы изобретете!протокол да близок, интерфейсы нет, омерзительный js нам не нужен.
протокол да близок, интерфейсы нет, омерзительный js нам не нужен.Ну я имел в виду что вы отстали от жизни лет на 10
JS конечно проблема, но уж что выросло то выросло
Ну я имел в виду что вы отстали от жизни лет на 10отстали от чего?
Тебе хочется оборачивать в один try куски из UI кода и куски не UI кода? А нафига это надо?я хочу, чтобы один поток управления был оформлен, как один поток кода, что позволит использовать нативные средства кода для организации потока управления (foreach, try/using, вызов функций)
если поток управления затрагивает и gui, и не-gui, то да try будет затрагивать и gui, и не gui
зы
использование нативных средств достигает две цели:
1. используются лучшие инструменты для описания (если нативные средства не лучшие, тогда значит нативные средства устарели и их необходимо заменить)
2. уменьшает стоимость сопровождение кода в два раза (нет необходимости поддерживать два разных набора конструкций для описания одного и того же
JS конечно проблема, но уж что выросло то вырослодля того, чтобы в продукте использовался js, не обязательно кодить на js
твои недостатки тоже не понял, кроме №3если вспомнить часть критериев хорошего кода, то видно, почему это недостатки:
писать код так, чтобы требовалось как можно меньше информации для того, чтобы представить как код будет себя вести
а. использовать как можно меньший "алфавит" (как можно меньше требуется усвоить дополнительной информации перед прочтением кода)
b. при чтении локального куска кода, как можно меньше информации требуется из других частей кода
да и он не недостаток, если приложение уже event-drivenоформление кода, как event-driven само со себе очень большой недостаток, не сильно лучше, чем многопоточность.
представить при этом по коду с минимум доп. информации, что будет реально происходить, и там, и там почти невозможно.
при этом исполнение, как event-driven - полезно.
для того, чтобы в продукте использовался js, не обязательно кодить на jsСпасибо, капитан!
Спасибо, капитан!да, не за что, коллега
1. используются лучшие инструменты для описания (если нативные средства не лучшие, тогда значит нативные средства устарели и их необходимо заменить)"лучшие инструменты для описания" сильно размытый и спорный термин
уменьшает стоимость сопровождение кода в два раза (нет необходимости поддерживать два разных набора конструкций для описания одного и того же
GUI код описывает свои задачи, не GUI код свои. Где одно и тоже два раза?
"лучшие инструменты для описания" сильно размытый и спорный терминможно ввести более-менее однозначный критерий для поиска лучшего инструмента: чем меньше информации для описания требуется, тем лучше.
да, такой критерий требует поправку на учет ограничений мышления человека (ограниченные способности для запоминания, распознавания и т.д. но критерий все равно при этом останется однозначным
GUI код описывает свои задачи, не GUI код свои. Где одно и тоже два раза?я говорил про два набора инструментов.
если для обработки ошибок есть инструмент try/catch, то зачем нужна какая-то другая конструкция, которая делает то же самое (обеспечивает передачу управления на спец. кусок кода при ошибке)?
если для описания последовательности действий используется последовательная запись операторов, то зачем нужна какая-то другая конструкция?
если для описания последовательности действий используется последовательная запись операторов, то зачем нужна какая-то другая конструкция?ну gui так никто не пишет, потому что последовательность действий там определяет в основном пользователь
я говорил про два набора инструментов.где удвоение стоимости разработки? У тебя выходит, если есть два инструмента, то работа стоит в два раза дороже. Хрень какая-то.
обеспечивает передачу управления на спец. кусок кода при ошибкеникто хорошо не определит термин "ошибка", поэтому это не подходит в качестве формулировки требований.
если для обработки ошибок есть инструмент try/catch
да, в языке есть конструкция try/catch. Она работает так то и так то, одной важной особенностью является то, что она раскручивает стек, прерывая выполнения функций. Да, есть ситуации, когда это полезно. Но нужно ли это при организации схемы запрос/ответ? Такой потребности пока не видно. Зачем может потребоваться раскручивать стек со смесью GUI и не GUI кода?
ну gui так никто не пишет, потому что последовательность действий там определяет в основном пользовательдо этого речь шла про один логический поток управления, а не про взаимодействие множества логических потоков управления.
до этого речь шла про один логический поток управления,нет, речь шла о том, чтобы в GUI можно было что-то делать, пока где-то в каком-то другом потоке работает запрос
нет, речь шла о том, чтобы в GUI можно было что-то делать, пока где-то в каком-то другом потоке работает запроси какой логической связкой это утверждение отрицает предыдущее утверждение?
Такими темпами вы скоро веб-интерфейсы изобретете!Следующим этапом будет клиент-серверная архитектура, как таковая, и гуи, работающие через dbus.
Но нужно ли это при организации схемы запрос/ответ? Такой потребности пока не видно. Зачем может потребоваться раскручивать стек со смесью GUI и не GUI кода?один из типичных сценариев gui (при этом эта та самая смесь gui и не-gui в едином потоке управления):
пользователь нажал кнопку
вывелось что команда начала выполняться (например, за-disable-илась кнопка)
запрос отправился на сервер
выводится процесс выполнения (например, бегает прогресс-бар или часы)
сервер прислал ответ, что запрос выполнился (успешно или неуспешно)
вывелся результат
(*)"очистилось" отображение, что команда выполняется, и отображение процесса выполнения (кнопка за-enable-илась обратно, прогресс-бар(часы) убрались)
при этом пункт со звездочкой должен выполниться гарантированно, в независимости от того, последовательность выполнилась как планировалось, или что-то пошло не так(пропал сервер, кончилась память(или диск) на клиенте при попытке сформировать запрос(обработать запрос) и т.д.)
и это есть стандартный паттерн захвата/очистки ресурса, который нативно оформляется, или через try/finally, или через using/destructor
отправка сообщения на сервер с последующим получением и выводом результата - это стандартный паттерн вызова функции(или метода)
это дает код вида:
пользователь нажал кнопку
вывелось что команда начала выполняться (например, за-disable-илась кнопка)
try
{
запрос отправился на сервер
выводится процесс выполнения
сервер прислал ответ, что запрос выполнился (успешно или неуспешно)
вывелся результат запроса
}
finally
{
(*)"очистилось" отображение [..]
}
или если использовать все перечисленные свертки:
пользователь нажал кнопку
using(показывать-что-команда-выполняется
{
экран.определенная-область = cервер.выполнить-запрос;
}
этот же сценарий можно описать тысячью разными способами: через event-driven, через асинхронные вызовы делегатов и т.д., но в них будет расти уровень "шума": будет расти доля слов, которые ничего полезного не описывают с точки зрения логической последовательности работы сценария
зы
во многих приложениях видно, что паттерн try/finally часто игнорируют в таких сценариях, и кнопки обратно не отжимаются, если что-то идет не так как планировалось (например, при временной пропаже соединения с сервером)
при этом пункт со звездочкой должен выполниться гарантированно, в независимости от того, последовательность выполнилась как планировалось, или что-то пошло не так(пропал сервер, кончилась память(или диск) на клиенте при попытке сформировать запрос(обработать запрос) и т.д.)вот здесь у тебя ошибка: пользователь может закрыть уже окно к тому времени
так как окна, которые нельзя закрыть, пока не выполнится какая-то длинная операция - жутко бесят
ну и ещё недостаток - в твоём коде нигде не указано, что во время
выполнения этой последовательности пользователь работает с гуи, и может поменять состояние чего-нибудь - то есть структура кода не отражает выполняемые действия
Кстати, async/await можно будет использовать внутри лямбд?
отправка сообщения на сервер с последующим получением и выводом результата - это стандартный паттерн вызова функции(или метода)вызов функции это вызов функции: делаем вызов, ждем результата, получаем результат, т.е. обычная синхронность. Полностью избавиться от различий между синхронным и асинхронным вызовом нельзя, это видно даже из общих соображений без привязки к какому-либо инструменту.
это дает код вида:
если синхронный вызов подвиснет на часок? Плохой инет там, например.
А то память кончиться может, а инет сдохнуть(или серверная часть синхронного вызова) не может? Как-то однобоко получается.
Тогда этот finaly не выполнится вообще. Да ещё и прогу придётся килить, а это может означать недописанные файлы, например.
Приходишь вот так в бухгалтерию, чтобы бумагу забрать, а они тебе "а у нас тут всё висит, нажимаешь кнопку, и ничего не проиходит". и сидишь ждёшь, а кильнуть нельзя - локальная база какая-нибудь может сломаться и потом нужно будет неделю ждать специалиста, чтобы он её разлочил.
где тут шум?
Я вообще не понимаю, в чём фишка делать "мегафункцию", которая и отправляет и обрабатывает. Это ж GUI. всё на событиях всё равно.
кнопку нажал - отослался запрос
пришёл ответ - вывели результат
пользователь нажал кнопку
try
{
создался дочерний процесс(принят запрос)
отправилось сообщение процессу
сменить состояние(запрос отправлен)
}
catch
{
сообщение о неудаче
сменить состояние(ожидание команды)
}
пользователь нажал кнопку "отменить" (или закрыл окно)
отсылается сообщение процессу помереть.
ждём 100мс
убиваем процесс
сменить состояние(ожидание команды)
процесс2-main
запрос отправился на сервер
сервер прислал ответ, что запрос выполнился (успешно или неуспешно)
отослался результат основному процессу
пришёл ответ
вывелся результат запроса
сменить состояние(ожидание команды)
сменить состояние(NEWSTATE)
STATE = NEWSTATE
отобразить состояние
отобразить состояние
если STATE == принят запрос
вывелось что команда начала выполняться (например, за-disable-илась кнопка)
если STATE == запрос отправлен
вывелось что команда начала выполняться2 (например, за-disable-илась кнопка)
если STATE == ожидание команды
"очистилось" отображение [..]
void ВыполнитьЗапрос
{
пользователь нажал кнопку
вывелось что команда начала выполняться (например, за-disable-илась кнопка)
try
{
запрос отправился на сервер
выводится процесс выполнения
сервер прислал ответ, что запрос выполнился (успешно или неуспешно)
вывелся результат запроса
}
finally
{
(*)"очистилось" отображение [..]
}
}
и будем его вызывать:
void Main
{
ВыполнитьЗапрос;
MessageBox.Show("Предлагаем пользователю нажать кнопку.") //а она задизейблена
}
Это я к тому, что такой код становиться необычным, его нельзя выделять в метод и ожидать обычное поведение.
вот здесь у тебя ошибка: пользователь может закрыть уже окно к тому времениКод модельный, понятное дело, что в реальном приложении всё будет намного сложнее. Кроме/вместо задизейбленной кнопки будет выводиться какой-нибудь диалог с кнопкой отмены или что-то в этом роде. Чем сложнее решаемая задача и чем больше состояний, тем хуже будет выглядеть код, в котором ломается control flow.
так как окна, которые нельзя закрыть, пока не выполнится какая-то длинная операция - жутко бесят
ну и ещё недостаток - в твоём коде нигде не указано, что во времяОбычно запросы простые и этого не нужно, главное, чтобы GUI не создавало у людей ощущения, что программа повисла. То есть достаточно отрисовки, анимации какого-нибудь progress bar и кнопки отмены.
выполнения этой последовательности пользователь работает с гуи, и может поменять состояние чего-нибудь - то есть структура кода не отражает выполняемые действия
если синхронный вызов подвиснет на часок? Плохой инет там, например.Use timeouts, Luke.
Я вообще не понимаю, в чём фишка делать "мегафункцию", которая и отправляет и обрабатывает. Это ж GUI. всё на событиях всё равно.
кнопку нажал - отослался запрос
пришёл ответ - вывели результат
Чего ты точно не понимаешь, так это сложности разработки программы при наличии этих состояний. О чём пишет : при выполнении запроса на сервер секции try/catch/finally тебе придётся разбить на три отдельных блока, при этом ломается и становится на порядки сложнее control flow.
То есть достаточно отрисовки, анимации какого-нибудь progress bar и кнопки отмены.Ну то есть окно ничего не делает, занимает экран (а то и фокус, если модальное только нагло показывает анимацию. Хочется взять и уебать этих ИТ-специалистов и их менеджера.
Ну это тогда же от платформы зависит - как там проще всего преобразовать синхронный вызов в асинхронный. Если надо руками создавать треды - ну значит платформа несколько недоделанная, надо добавить эту свою библиотечную функцию и снова забыть про треды. Но помнить про реентерабельность.
Ну то есть окно ничего не делает, занимает экран (а то и фокус, если модальное только нагло показывает анимацию. Хочется взять и уебать этих ИТ-специалистов и их менеджера.Если речь идёт о запросах, которые выполняются более 10 секунд, то да, GUI нужно проектировать так, чтобы можно было работать с программой, пока выполняется долгий запрос. Если речь идёт о быстрых запросах, то лучше блокировать весь GUI (конечно же нарисовав кнопку отмены потому что иначе разработчики наделают такое количество ошибок, что закачаешься. Я на практике опробовал кучу методов, видел во что они превращаются при наращивании функционала, и теперь стараюсь придерживаться простого сценария.
Ну это тогда же от платформы зависит - как там проще всего преобразовать синхронный вызов в асинхронный. Если надо руками создавать треды - ну значит платформа несколько недоделанная, надо добавить эту свою библиотечную функцию и снова забыть про треды. Но помнить про реентерабельность.Если рассматривать любую библиотеку общего назначения, то в ней, скорее всего, не будет возможности получать сообщения прямо в GUI. Потому что GUI библиотеки могут быть любыми, никто не будет выпускать библиотеку для HTTP запросов отдельно для Windows Forms и отдельно для WPF.
Если вести речь про .NET, то синхронный вызов в асинхронный переделать очень просто. И потоки руками создавать не придётся, но придётся про них помнить. То есть в этом случае мы не уходим от сценария, когда разработчик должен знать, как это работает.
Use timeouts, Luke.
Синхронный вызов может быть реализован в закрытой библиотеке, которая свои таймауты устанавливает. И потом, можно не все учесть, может быть(или появиться) баг в библиотеке.
Второе: это можно быть супер крутой запрос в БД, который длится 2-3минуты. таймаут в этом случае будет минут 10-20, так?
Во время выполнения запроса, сисадмин Василий, решил перенастроить роутинг. Когда ему позвонили спросить, что "ничего не работает", он предложил перезапустить приложение.
Я один раз где-то вот минут 10 стоял около банкомата, ждал пока он созреет. Бабло вот списалось, а денег не дали.(это не аргумент против тредов, это намёк, что такие таймауты реальны вполне)
Я опаздывал.
Я не прочь был бы нажать "не надо денег, отдай карточку, я опаздываю". Тогда бы мне не пришлось ехать в банк и писать заявление, чтобы денеги вернули.
Чего ты точно не понимаешь, так это сложности разработки программы при наличии этих состояний
понимаю, что это немного сложнее в данном случае. В общем случае это значительно сложнее. Но в общем случае, либо задача разбивается на несколько независимых состояний, тогда сводится к более простой, либо без централизованной обработки состояний её решить намного проще.
при выполнении запроса на сервер секции try/catch/finally тебе придётся разбить на три отдельных блока, при этом ломается и становится на порядки сложнее control flow.
От того, что все 3 блока внутри одного try, меньше ломаться не станет.
была поставлена задача: что бы ни произошло, должен выполниться finaly. Я привёл пример, когда синхронный вызов подвисает. В этом случае finaly никогда не выполнится.
Или мы предполагаем, что синхронный вызов подвиснуть не может ни при каких обстоятельствах?
Если речь идёт о быстрых запросах, то лучше блокировать весь GUI
...например, выполняя запрос в GUI-треде.
Если рассматривать любую библиотеку общего назначения, то в ней, скорее всего, не будет возможности получать сообщения прямо в GUI. Потому что GUI библиотеки могут быть любыми, никто не будет выпускать библиотеку для HTTP запросов отдельно для Windows Forms и отдельно для WPF.Есть же системные средства. Обычные сообщения Windows, которые WM_что-то. Для *nix такие библиотеки интегрируются в select loop, в него же идут сообщения от X11. Своей обёрткой это наверное придётся обернуть, но один раз.
Если вести речь про .NET, то синхронный вызов в асинхронный переделать очень просто. И потоки руками создавать не придётся, но придётся про них помнить. То есть в этом случае мы не уходим от сценария, когда разработчик должен знать, как это работает.Поясни плиз суть проблемы, не могу себе представить, зачем помнить про реализацию.
Синхронный вызов может быть реализован в закрытой библиотеке, которая свои таймауты устанавливает. И потом, можно не все учесть, может быть(или появиться) баг в библиотеке.В этом случае отдельный процесс и ломаный control flow тебе ничем не поможет.
Второе: это можно быть супер крутой запрос в БД, который длится 2-3минуты. таймаут в этом случае будет минут 10-20, так?Нет, читай выше ответ -у.
Но в общем случае, либо задача разбивается на несколько независимых состояний, тогда сводится к более простой, либо без централизованной обработки состояний её решить намного проще.Выше я уже писал про паттерн State. Да, это работает, но удобно и легко поддерживается только в частных случаях. Очень часто связи между состояниями неявные, один вполне линейный процесс приходится собирать по кубикам в разных местах или в разных файлах. Переходы между состояниями тоже бывают неявными, иногда тяжело понять, по какому пути пойдёт выполнение.
От того, что все 3 блока внутри одного try, меньше ломаться не станет.Если все три блока внутри одной линейной конструкции, то control flow у тебя перед глазами. Если они разнесены на три разные функции, то код читать намного тяжелее.
была поставлена задача: что бы ни произошло, должен выполниться finaly. Я привёл пример, когда синхронный вызов подвисает. В этом случае finaly никогда не выполнится.Если вызов подвисает, то тебе не поможет ни одна модель. (Хотя лично я не видел библиотек, которые что-то вызывают по сети и не поддерживают отмены, а если увижу, то не буду использовать.)
Есть же системные средства. Обычные сообщения Windows, которые WM_что-то. Для *nix такие библиотеки интегрируются в select loop, в него же идут сообщения от X11. Своей обёрткой это наверное придётся обернуть, но один раз.Системные средства доступны на самом низком уровне, под .NET пользоваться очередью сообщений не удобно, можешь получить ещё больше проблем, чем при создании отдельного потока. А если не умеешь этого делать, то и на Си++ будут ошибки: поищи темы про "когда я двигаю окно, у меня перестаёт доставляться сообщение WM_TIMER", за последний год-два здесь была пара таких сообщений.
По этой и вышеназванным причинам в библиотеку для HTTP вызовов вряд ли будут интегрировать очередь GUI сообщений.
--------------------------------------------------------------------------------Не про реализацию, а про то, как работает. Асинхронный вызов будет выполняться в отдельном потоке, поэтому необходимо использовать мьютексы, очередь сообщений для передачи данных в GUI, и так далее.
Если вести речь про .NET, то синхронный вызов в асинхронный переделать очень просто. И потоки руками создавать не придётся, но придётся про них помнить. То есть в этом случае мы не уходим от сценария, когда разработчик должен знать, как это работает.
--------------------------------------------------------------------------------
Поясни плиз суть проблемы, не могу себе представить, зачем помнить про реализацию.
Второе: это можно быть супер крутой запрос в БД, который длится 2-3минуты. таймаут в этом случае будет минут 10-20, так?
Нет, читай выше ответ -у.
я хочу отменить запрос, а не продолжать работать с программой.
Если вызов подвисает, то тебе не поможет ни одна модель. (Хотя лично я не видел библиотек, которые что-то вызывают по сети и не поддерживают отмены, а если увижу, то не буду использовать.)
Как ты себе это представляешь? Запускается дополнительный тред(или в основном в котором вызывается cancel_last_query и синхронный вызов(который в соседнем треде идёт) завершается с ошибкой ERR_USER_CANCELED?
я хочу отменить запрос, а не продолжать работать с программой.Нажимаешь кнопку "Отмена". В случае отдельного потока в .NET можно просто закрыть сетевой ресурс прямо из GUI потока.
Как ты себе это представляешь? Запускается дополнительный тред(или в основном в котором вызывается cancel_last_query и синхронный вызов(который в соседнем треде идёт) завершается с ошибкой ERR_USER_CANCELED?Найди тему, где я объяснял green-у, как может быть реализована отмена операции, которая идёт в другом потоке. Можно, как я написал выше, просто проставить флаг и закрыть сокет, получить исключение во втором потоке, отобразить его в GUI.
Можно, как я написал выше, просто проставить флаг и закрыть сокет, получить исключение во втором потоке, отобразить его в GUI
Жестоко. Но всё-таки сужается множество возможных библиотек для использования до тех которые не подвисают или имеют средства аварийно останавливаться пользователем.
Асинхронный вызов будет выполняться в отдельном потоке, поэтому необходимо использовать мьютексы, очередь сообщений для передачи данных в GUI, и так далее.Ну вызов получил параметры, отослал их серверу, ждёт-ждёт-ждёт, получает ответ с результатом, отсылает GUI сообщение со ссылкой на результат. Мутексов никаких, очередь - она та же самая, что у всего остального GUI. Ну да, надо помнить, что нельзя просто так результат зафигачить в глобальную переменную (типа взять главное окно, и в нём поле) - надо дать ссылку на него GUI-треду, пусть разбирается - ты про это?
От того, что все 3 блока внутри одного try, меньше ломаться не станет.Есть ещё одна хитрость: можно сделать асинхронные вызовы, запуская очередь GUI сообщений для ожидания ответа от сервера. Тогда control flow остаётся максимально прозрачным. Вот каким может быть пример неблокирующего вызова из GUI на Windows Forms: userList = ServiceManager.UserService.GetAll Логика работы весьма простая: во время вызова GetAll отображается модальный диалог "Идёт запрос на сервер...", бесконечный прогресс бар или другая анимация, и кнопка "Отмена", исключения перевыкидываются вызывающему потоку. При этом запрос выполняется в другом потоке, а у пользователя не создаётся ощущения, что программа подвисла. Для того, чтобы сделать такую механику, нужны базовые знания по многопоточному программированию.
Если метод GetAll должен провести какую-то обработку данных, про которую мы рассуждали в нашем примере, то во время выполнения этой обработки внутри реализации GetAll нужно будет помнить, что доступ к данным идёт из другого потока.
Жестоко. Но всё-таки сужается множество возможных библиотек для использования до тех которые не подвисают или имеют средства аварийно останавливаться пользователем.Множество библиотек, которые я использую последние пять-шесть лет, при этом не сокращается. Таймаут или отмену поддерживает вообще всё, что имеет мало мальскую значимость в closed/open source.
Ну вызов получил параметры, отослал их серверу, ждёт-ждёт-ждёт, получает ответ с результатом, отсылает GUI сообщение со ссылкой на результат. Мутексов никаких, очередь - она та же самая, что у всего остального GUI. Ну да, надо помнить, что нельзя просто так результат зафигачить в глобальную переменную (типа взять главное окно, и в нём поле) - надо дать ссылку на него GUI-треду, пусть разбирается - ты про это?Да, примерно про это.
ну значит надо, чтоб твоя обёртка, которая соединяет асинхронные вызовы с сообщениями GUI, делала ровно это - и тогда забыть про то, что внутри
Правда там обработки нет. Но её можно прямо в том же процессе и делать, отослав основному процессу сообщение: "данные получены, идёт обработка".
Я всё равно не понимаю, в чём такая ценность того, что 3 строки написаны внутри одного try. При чём друг с другом совсем не связаны в общем-то.
поищи темы про "когда я двигаю окно, у меня перестаёт доставляться сообщение WM_TIMER", за последний год-два здесь была пара таких сообщений.нашёл
это аргумент в пользу системных средств - стандартный message loop внутри winapi обработает всё правильно
ну значит надо, чтоб твоя обёртка, которая соединяет асинхронные вызовы с сообщениями GUI, делала ровно это - и тогда забыть про то, что внутриДа, выше я уже описал красивую механику для этого. И я продолжаю считать, что профессиональный разработчик, получающий сегодня ~120к на руки, должен понимать, как это работает; должен уметь написать реализацию такой фигни с нуля; и должен быть способен исправить ошибку, которую его коллега может сделать в этой библиотеке.
нашёлЭто аргумент в пользу чтения документации перед тем, как что-то делать. Если мне нужно использовать WM_USER + X, то я делаю это правильно, и моя программа продолжает работать, когда кто-то двигает окно.
это аргумент в пользу системных средств - стандартный message loop внутри winapi обработает всё правильно
посмотри мой пример. там все три блока идут подряд. Никуда лазить не нужно.Одно дело, когда ты видишь try/catch/finally, другое дело, когда у тебя три функции. Первое проанализировать намного проще.
Правда там обработки нет. Но её можно прямо в том же процессе и делать, отослав основному процессу сообщение: "данные получены, идёт обработка".
void UpdateUserList
{
try
{
userList = ServiceManager.UserService.GetAll;
}
catch (Exception e)
{
LOG.Write(e);
MessageBox.Show("Blablabla");
}
}
Я всё равно не понимаю, в чём такая ценность того, что 3 строки написаны внутри одного try. При чём друг с другом совсем не связаны в общем-то.Ценность этого проявляется тогда, когда у тебя на поддержке несколько мегабайт кода в production. Запросов в одном экране GUI может быть десяток-другой. Если на каждый написано по три функции, пусть и в виде вложенных делегатов, ты замучаешься анализировать такой код.
дык не плати им 120 пока не научатся
Ценность этого проявляется тогда, когда у тебя на поддержке несколько мегабайт кода в production.получается, что Java тут сильно сливает, у нее же нет yield-а?
получается, что Java тут сильно сливает, у нее же нет yield-а?Не знаю, с чем связаны твои вставки про Java в каждую затычку. Я уже давно утверждал, что Java, как язык, хуже. Тем не менее, под Java выпущено несколько интересных framework-ов, которые добавляют асинхронные вызовы без необходимости менять код на асинхронный. Поэтому асинхронный код получается даже более красивым и простым, чем в C#, и именно в этой области сильно сливает скорее C#.
Если на каждый написано по три функции, пусть и в виде вложенных делегатов, ты замучаешься анализировать такой код.на первый взгляд (не уверен различие будет только в количестве фигурных скобочек. А анализ try охватывающего код, выполняющийся в нескольких потоках, тоже не такая уж простая задача.
на первый взгляд, различие будет только в количестве фигурных скобочек. А анализ try охватывающего код, выполняющийся в нескольких потоках, тоже не такая уж простая задача.уже написал: если у тебя тонны этих фигурных (и не только) скобочек, то это начинает влиять на качество анализа и читаемость кода.
Не знаю, с чем связаны твои вставки про Java в каждую затычку.это чтобы поддержать твой стиль общения.
Тем не менее, под Java выпущено несколько интересных framework-ов, которые добавляют асинхронные вызовы без необходимости менять код на асинхронный. Поэтому асинхронный код получается даже более красивым и простым, чем в C#, и именно в этой области сильно сливает скорее C#.
Как на этих фреймворках выглядит код, который без yield-а требуется три блока try?
(и не только)на первый взгляд, только
Все же очень странно, что количество скобочек так сильно влияет на твое восприятие.
это чтобы поддержать твой стиль общения.В каждой теме при неудачной попытке о чём-то поспорить, ты обязательно упоминаешь Java. Мой стиль общения тебе поддержать не удаётся: ты по-нормальному не можешь ни слиться, ни отстоять свою точку зрения.
Как на этих фреймворках выглядит код, который без yield-а требуется три блока try?Задай вопрос по-русски.
ты по-нормальному не можешь ни слиться, ни отстоять свою точку зрения.ты ровно про себя написал
ты ровно про себя написалВот именно, я написал про себя и про то, что ты не можешь ни так, ни эдак.
Задай вопрос по-русски.попытайся включить голову
на первый взгляд, толькоОтветь, что из этого является фигурными скобочками: ( ) { } =>
Все же очень странно, что количество скобочек так сильно влияет на твое восприятие.Было бы странно, если бы количество всякой мишуры не влияло на моё восприятие. Напиши, как мой код выглядит в твоём стиле:
void UpdateUserList
{
try
{
userList = ServiceManager.UserService.GetAll;
}
catch (Exception e)
{
LOG.Write(e);
MessageBox.Show("Blablabla");
}
}
: ... ты ...
Вот именно, я написал про себя и про то, что ты не можешь ни так, ни эдак.Лол, всё же у тебя поразительное строение мозгов, заточенное только на тролинг, даже готов смысл слово “ты” поменять.
попытайся включить головуОк, давай попытаемся включить голову.
Как на этих фреймворках выглядит код, который без yield-а требуется три блока try?Я понимаю, что ты учился в сельской школе, а на ВМК и МехМат берут фактически без проверки сочинения. Но ради уважения к себе, своему языку и людям, которые вынуждены читать твои опусы, следовало бы научиться выражать свои мысли хотя бы сопряжёнными частями речи. Для этого стоит прочитать вслух то, что ты написал. Заметь, я молчу про "тся" и "ться" - это нерешаемая кодерами задача. Но стоило бы пояснить, к чему относится глагол "требуется" в твоей фразе, и как последние четыре слова связаны с первой её частью? Любая, даже самая молодая учительница по русскому языку, подчеркнула бы эту фразу красной ручкой и поставила бы Р (речевая ошибка) на полях.
Короче, я предлагаю тебе написать асинхронный код с yield и тремя блоками try или что ты там хотел спросить.
Лол, всё же у тебя поразительное строение мозгов, заточенное только на тролинг, даже готов смысл слово “ты” поменять.Поразительное строение мозгов у того, кто не мыслит логически и не пытается понять, что ему написали.
Мой стиль общения тебе поддержать не удаётся: ты по-нормальному не можешь ни слиться, ни отстоять свою точку зрения.Поясняю смысл этой фразы: я в своём стиле общения могу либо по-нормальному слиться, либо отстоять свою точку зрения. Ты не можешь сделать ни первого, ни второго, и поэтому мой стиль общения тебе недоступен.
Поясняю смысл этой фразы: я в своём стиле общения могу либо по-нормальному слиться, либо отстоять свою точку зрения. Ты не можешь сделать ни первого, ни второго, и поэтому мой стиль общения тебе недоступен.читай что тебе пишут, "поддержать" это не значит перенять твой стиль общения, ни в коем случае . Попытки с тобой по хорошему приводят к тому, что ты отсылаешь без указания прямых ссылок. Поэтому приходится как-то поддерживать твои "эмоции" соответствующими вопросами.
Короче, я предлагаю тебе написать асинхронный код с yield и тремя блоками try или что ты там хотел спросить.здрасьте, код уже написан -ем и мы его тут обсуждаем
читай что тебе пишут, "поддержать" это не значит перенять твой стиль общения, ни в коем случаеДля того, чтобы что-то поддержать, надо понимать, что ты поддерживаешь. Каждый раз ты упоминаешь Java, и каждый раз получашь от неё по носу из-за отсутствия знаний по теме. Логично бы выбрать что-то другое, но ты продолжаешь то ли кусать кактус, то ли пугать ежа голой жопой.
здрасьте, код уже написан -ем и мы его тут обсуждаемПожалуйста, приведи ссылку на код, написанный , в котором есть yield и три блока try.
Для того, чтобы что-то поддержать, надо понимать, что ты поддерживаешь. Каждый раз ты упоминаешь Java, и каждый раз получашь от неё по носу из-за отсутствия знаний по теме. Логично бы выбрать что-то другое, но ты продолжаешь то ли кусать кактус, то ли пугать ежа голой жопой.короче, ты очередной раз перешел на личности, понятно, сливаешь
короче, ты очередной раз перешел на личности, понятно, сливаешьСлово "сливаешь" ты пишешь каждый раз, когда у тебя заканчиваются аргументы. А стоило бы писать, когда ты убедил оппонента и аудиторию в своей правоте.
Пожалуйста, приведи ссылку на код, написанный , в котором есть yield и три блока try.код -я вот
это псевдокод, но (насколько я понимаю) его можно осуществить на C# либо через yield, либо через async/await (в следующей версии).
Если без yield, то потребуется три try-я: на первый GUI блок, на блок в другом потоке, на последний GUI блок.
Хотелось бы увидеть что и как там на Java в этом плане.
на первый GUI блокэтот блок может быть пустым, но не суть
Оч хуево в яве без yieldа
это псевдокод, но (насколько я понимаю) его можно осуществить на C# либо через yield, либо через async/await (в следующей версии).Этот псевдокод можно реализовать без yield и без async, и я привёл наглядный пример.
Если без yield, то потребуется три try-я: на первый GUI блок, на блок в другом потоке, на последний GUI блок.Нет, если без yield, то потребуется один try. Про yield речь зашла только в рамках реализации continuations.
Хотелось бы увидеть что и как там на Java в этом плане.Если речь идёт про эмуляцию continuations, то вместо конструкций типа yield в Java используют фреймворки, которые перезаписывают байт код. Например, из известных: RIFE и JavaFlow.
я несколько потерял нить вашего спора, но наверно поддержу шурека(!).Если все друг-друга понимают, то речь про реализацию continuations в языке, где их нет; и про то, что Java якобы сливает в этом плане.
Оч хуево в яве без yieldаЯ использовал его только для эмуляции continuations , и поэтому не вижу разницы. Расскажи, для каких ситуаций его используешь ты?
для простого создания итераторов очевидно
для простого создания итераторов очевидноМожно более детально рассказать, какие задачи ты этим решаешь? Пишешь свои коллекции? Приведи пример кода, чтобы я мог до конца понять, почему тебе хуёво без yield. Я бы переписал его без yield, чтобы посмотреть, что получится.
Нет, если без yield, то потребуется один try.без yield и без запуска другой очереди GUI сообщений? Приведи ссылку на твое сообщение, пожалуйста.
Если речь идёт про эмуляцию continuations, то вместо конструкций типа yield в Java используют фреймворки, которые перезаписывают байт код. Например, из известных: RIFE и JavaFlow.код привести можешь?
код привести можешь?Почему ты не приводишь код на C#, а я должен приводить код на Java? Отгугли и то, и другое, объективно проанализируй, напиши пару туториалов и тогда аргументированно наезжай.
без yield и без запуска другой очереди GUI сообщений? Приведи ссылку на твое сообщение, пожалуйста.Вот ещё раз код, если ты его потерял. Этот код вызывает сервис, отображая диалог с прогресс баром и кнопкой отмены на экране:
void UpdateUserList
{
try
{
userList = ServiceManager.UserService.GetAll;
}
catch (Exception e)
{
LOG.Write(e);
MessageBox.Show("Blablabla");
}
}
public abstract class MyCoolReader
{
//шоб чуть меньше доебывались
protected abstract byte[] CreateBuffer;
//тут мы както будем раскладывать данные из буфера в объект
protected abstract BizObject ParseToObject(byte [] buffer);
public IEnumerable<BizObject> Read(Stream stream)
{
var buffer = CreateBuffer;
while(true)
{
if stream.Read(buffer) == 0
{
break;
}
yield return ParseToObject(buffer);
}
}
}
someButton.Enable = false; //пример UI-ного кода в начале запроса
someButton.Enable = true; //пример UI-ного кода по окончанию
я бы хотел увидеть, где здесь строчки:Очевидно, что они выполняются внутри вызова GetAll
Пользуясь случаем, поздравляю всех коллег с профессиональным праздником!
Пользуясь случаем, поздравляю всех коллег с профессиональным праздником!Спешишь. Завтра только.
Очевидно, что они выполняются внутри вызова GetAllОк. Единственно, отмечу для себя, что UserService имеет доступ к someButton (это ни хорошо и ни плохо, просто отметил).
Если твой код имеет отношение к нашей дискуссии, то надо раскрыть некоторые детали метода GetAll: где в нем отделение работы с тредами от бизнес-логики и UI-я?
Ок. Единственно, отмечу для себя, что UserService имеет доступ к someButton (это ни хорошо и ни плохо, просто отметил).С точки зрения ООП он ничего не имеет, так что я без понятия, что ты там для себя отметил. Если тебе требуется изменять доступность кнопки без модальных диалогов на экране, то можно написать это руками в моём коде: ставишь Enabled = false внутри try и = true внутри finally.
Если твой код имеет отношение к нашей дискуссии, то надо раскрыть некоторые детали метода GetAll: где в нем отделение работы с тредами от бизнес-логики и UI-я?Метод GetAll реализован только на сервере, так что я без понятия, о чём ты.
Метод GetAll реализован только на сервере, так что я без понятия, о чём ты.ты же только что написал, что someButton.Enable = false внутри GetAll. Эта строчка не скомпилируется на сервере .
Ох, еще и код писать. Ну ок:Понятно, это достаточно интересный пример. У меня получается несколько альтернатив без yield. Не знаю, можно или нельзя из-за этого говорить про то, что жить без yield невозможно. На Java решение с итератором такое, не знаю, настолько ли страшнее твоего варианта, чтобы написать "очень хуёво" (понятное дело, что реализация итератора в проекте уедет в отдельный класс и в итоге придётся писать гораздо меньше букв):
public abstract class IterationReader implements Iterable<BizObject> {
protected abstract byte[] getBuffer;
protected abstract BizObject parse(byte[] buffer);
public Iterator<BizObject> iterator(final InputStream stream) {
return new Iterator<BizObject> {
final byte[] buffer = getBuffer;
boolean hasNext = false;
public boolean hasNext {
// won't handle exceptions for clarity
return hasNext = (stream.read(buffer) != 0);
}
public BizObject next {
if (!hasNext) throw new NoSuchElementException;
return parse(buffer);
}
public void remove {
throw new UnsupportedOperationException;
}
};
}
}
Edit:
return new ReadonlyForwardIterator<BizObject> {
final byte[] buffer = getBuffer;
@Override
protected boolean updateNext {
// won't handle exceptions for clarity
return stream.read(buffer) != 0;
}
@Override
protected BizObject getNext {
return parse(buffer);
}
};
for (BizObject obj : new IterationReaderImpl {
}
Вот без чего я считаю "хуёво", так это без лямбд хотя бы в варианте C#. В текущем моём проекте необходимость делать итераторы вручную полностью исчезает из-за использования Linq. То же самое есть и для Java, и вот оно реально выглядит некрасиво.
ты же только что написал, что someButton.Enable = false внутри GetAll. Эта строчка не скомпилируется на сервере .Всё скомпилируется, потому что этой строчки никогда не будет на сервере. Тебе ещё требуется информация или признаешь, что ты без понятия, как сделать такую простенькую схему неблокирующих RPC? Или, вдруг, до тебя уже дойдёт, как это сделано?
Всё скомпилируется, потому что этой строчки никогда не будет на сервере. Тебе ещё требуется информация или признаешь, что ты без понятия, как сделать такую простенькую схему неблокирующих RPC? Или, вдруг, до тебя уже дойдёт, как это сделано?Поскольку ты не признал противоречия в твоих двух соседних постах, имею полное право сказать, что ты слил.
Поскольку ты не признал противоречия в твоих двух соседних постах, имею полное право сказать, что ты слил.Я их не признал, потому что их нет: button.Enabled = false/true может выполняться внутри вызова GetAll и это происходит на стороне клиента. При этом метод GetAll в коде реализован только на сервере. Если ты не знаешь, как это делается, то так и напиши, а не пытайся засчитывать свои собственные сливы другим.
Я не знаю, мне расскажи!
Я их не признал, потому что их нет: button.Enabled = false/true может выполняться внутри вызова GetAll и это происходит на стороне клиента. Если ты не знаешь, как это делается, то так и напиши, а не пытайся засчитывать свои собственные сливы другим.ага, если только смысл слова A заменить на смысл слова B, т.е. пользоваться твоими приемами построения утверждений, то да, возможно все
Я не знаю, мне расскажи!Да по сути, если почитать всю тему внимательно, последние две страницы, то можно понять и без детального рассказа. Я потом более детально опишу происходящее, если потребуется.
Я не знаю, мне расскажи!почти уверен, что Майк отмажется сейчас, вот смотри
ага, если только смысл слова A заменить на смысл слова B, т.е. пользоваться твоими приемами построения утверждений, то да, возможно всеУ меня простой вопрос: ты знаешь, как это делается, или нет?
я угадал
почти уверен, что Майк отмажется сейчас, вот смотриПросто скажи, что ты не знаешь, чтобы сохранить это для истории. А потом я напишу, как можно это всё сделать.
У меня простой вопрос: ты знаешь, как это делается, или нет?делать что? Какую задачу решаешь ты мне стало не понятно.
Запросы к серверу с неблокирующим UI-ем, да, естественно, делаю уже много лет.
делать что? Какую задачу решаешь ты мне стало не понятно.Я привёл фрагмент кода, на который ты наезжаешь и пытаешься засчитать мне какие-то сливы. При этом сливы ты почему-то пытаешься засчитать за утверждения: GetAll в коде реализуется только на стороне сервера; GUI во время вызова не блокируется, напротив, выводится диалог с marquee прогресс баром и кнопкой "Отмена", которая работает и отменяет запрос на сервер.
Я отталкиваюсь от примера -я, поскольку это, действительно, типичный пример, если ты напишешь в своем коде someButton.Enable = true, будет понятнее.
Да по сути, если почитать всю тему внимательно, последние две страницы, то можно понять и без детального рассказа.Та мне детально не надо, мне бы в общих чертах понять.
У тебя есть какой-то клиентский прокси-класс, реализующий тот же интерфейс, что и серверный Service, и выполняющий асинхронный вызов с показом прогресса?
А, раз ты говоришь, что на клиенте GetAll ты не реализуешь никак, то этот прокси-класс у тебя генерируется автоматически через какой-нибудь reflection?
Я отталкиваюсь от примера -я, поскольку это, действительно, типичный пример, если ты напишешь в своем коде someButton.Enable = true, будет понятнее.Для простоты предлагаю написать Enabled=false в моём коде прямо перед вызовом GetAll затем добавить finally и прописать там true. Но, как я написал выше для , я такой подход не практикую и всячески против него.
void UpdateUserList
{
try
{
button.Enabled = false;
userList = ServiceManager.UserService.GetAll;
}
catch (Exception e)
{
LOG.Write(e);
MessageBox.Show("Blablabla");
}
finally
{
button.Enabled = true;
}
}
Ну что, теперь ты "вдруг" "давно понял", как всё работает, или нет?
какой тип возвращаемого значения у GetAll?
А, раз ты говоришь, что на клиенте GetAll ты не реализуешь никак, то этот прокси-класс у тебя генерируется автоматически через какой-нибудь reflection?Да, это одна из возможных реализаций данного подхода. Да и вообще, разве ты сам, руками, делаешь реализацию сервиса на клиенте?
какой тип возвращаемого значения у GetAll?Поифгу, пусть будет List<UserDto>
У меня получается несколько альтернатив без yield. Не знаю, можно или нельзя из-за этого говорить про то, что жить без yield невозможно.эээ, не. Я не говорил "невозможно" Очевидно возможно, т.к. писалиж раньше без него. И даже без явы(сишарпа )
. На Java решение с итератором такое, не знаю, настолько ли страшнее твоего варианта, чтобы написать "очень хуёво"
На мой взгляд страшнее, многа буков. (Кстати на питоне этот код будет выглядеть еще более выразительно). Не спорю, что вопрос дискуссионный. Но мне лично нравится лаконичная запись с елдом. Плюс, общее решение для такого итератора врядли можно будет упаковать в один класс, скорей будет пара-тройка на несколько "общих" ситуаций
Отдельно напрягает вот этот метод:
public boolean hasNext {
// won't handle exceptions for clarity
return hasNext = (stream.read(buffer) != 0);
}
т.к. получается эдакий сайд-эффект, поэтому второе решение красивей, но тоже громозденько.
Нащщет лямбд согласен, ага.
На мой взгляд страшнее, многа буков.Даже если убрать сайд эффекты, то при использовании общего класса придётся рефакторить твой код, чтобы в нём было меньше букафф. Что характерно, я с yeild вообще не сталкиваюсь, потому что проще передать в виде IEnumerable<T> какую-нибудь read only коллекцию, что на C#, что в Java. В общем, хуёво это или нет, действительно скорее тема холивара.
Да, у меня куцые знания в многопоточности. Мне приходят на ум только извращенные варианты реализации того, как у тебя написано. Это можно сделать, но сильно извращенно. Ок, как ты это делаешь?
то характерно, я с yeild вообще не сталкиваюсь, потому что проще передать в виде IEnumerable<T> какую-нибудь read only коллекцию,так весь смак возникает в том случае, если на момент вызова метода ты не знаешь, сколько же элементов будут засунуты в эту коллекцию.
Да, это одна из возможных реализаций данного подхода. Да и вообще, разве ты сам, руками, делаешь реализацию сервиса на клиенте?Да это же твоя собственная версия ChannelFactory<TInterface>, которая связана с UI!
Респект за обфускацию.
так весь смак возникает в том случае, если на момент вызова метода ты не знаешь, сколько же элементов будут засунуты в эту коллекцию.Да, просто у меня такие задачи возникают только в серверной части при реализации continuations. Но на практике я почему-то не сталкиваюсь с необходлимостью использовать yield-ы.
Это можно сделать, но сильно извращенно.Давай не наезжать на извращённость, continuations через yield тоже достаточно извращён, но писать асинхронный код без этого ты долбанёшься.
Да, у меня куцые знания в многопоточности. Мне приходят на ум только извращенные варианты реализации того, как у тебя написано. Это можно сделать, но сильно извращенно. Ок, как ты это делаешь?Идея реализации примерно такая:
1. Любым доступным методом делаешь прокси для интерфейса твоего сервиса. Нас интересует тема AOP, поэтому можно использовать готовые решения, например, из Spring.NET; если нет сервиса, всё то же самое можно проделать и для HttpRequest/WebRequest.
2. Interceptor (или наш proxy код выполняемый before execution, начинает новую очередь сообщений.
3. Для того, чтобы избежать огроменной кучи проблем, которые порождает event driven подход, я всегда делаю модальный диалог с кнопкой отмены. Хотя это не обязательно, и можно написать полностью event driven GUI, но я ни разу не видел, чтобы такие реализации нормально работали при наличии сложной логики в GUI.
4. Внутри нового цикла сообщений (для WindowsForms можно использовать Form.Shown или Form.BeginInvoke) делегируем вызов сервису в отдельном потоке, это можно сделать любым способом, например BeginInvoke.
5. Если пользователь нажимает кнопку "Отмена", то закрываем диалог ожидания (останавливаем наш вложенный цикл обработки сообщений) и выкидываем, например, UserCanceledException.
6. Если вызов завершился, он переезжает в GUI, закрывая диалог ожидания (останавливая наш вложенный цикл обработки сообщений).
7. Interceptor возвращает результат или перевыкидывает исключение, которое произошло при вызове в другом потоке.
8. Опционально пишется exception interceptor, в котором можно обрабатывать общие ошибки. Например, при получении SocketException с каким-то кодом можно написать сообщение "бла бла нет связи с сервером блабла, повторить запрос?" и ляпнуть пару кнопок "Да"/"Нет". Если пользователь кликает "Да", то выполняется логика первого interceptor-а, который повторяет запрос на сервер.
Итого по коду фреймворка в одном из моих проектов, количество строк брутто: модальный диалог presenter 225 строк, модальный диалог view 65 строк, класс с interceptor 140 строк. Не вижу ничего извращённого, а даже наоборот, появляется возможность писать красивый клиентский код без ломаного логического control flow.
Да это же твоя собственная версия ChannelFactory<TInterface>, которая связана с UI!Метод подходит для всего: долгих вычислений, запросов через WebRequest/HttpRequest, WCF, и так далее. То есть это далеко не собственная версия ChannelFactory<TInterface>, если ты имеешь в виду WCF.
Давай не наезжать на извращённость, continuations через yield тоже достаточно извращён, но писать асинхронный код без этого ты долбанёшься.ну у нас вполне рабочей схемой является вот .
Мы не сталкивались с ситуацией, когда часто требовалось охватывать try-ем UI и не UI код. Т.е. проблема, которую затронул , она теоретически есть, но на практике показатели приложения/девелопмента/саппорта вряд ли значительно улучшатся при наличии такой фичи.
Ну, в конце концов, JQuery живет на таком подходе и ничего народ доволен.
... делегируем вызов сервису в отдельном...После нажатия "Отмена" поток, в котором идет обращение к сервису, остается занятым?
5. Если пользователь нажимает кнопку "Отмена", то закрываем диалог ожидания (останавливаем наш вложенный цикл обработки сообщений) и выкидываем, например, UserCanceledException.
После нажатия "Отмена" поток, в котором идет обращение к сервису, остается занятым?А как ты думаешь?
3. Для того, чтобы избежать огроменной кучи проблем, которые порождает event driven подход, я всегда делаю модальный диалог с кнопкой отмены. Хотя это не обязательно, и можно написать полностью event driven GUI, но я ни разу не видел, чтобы такие реализации нормально работали при наличии сложной логики в GUI.Да, давать возможность пользователю везде кликать во время запроса, это сильное усложнение даже просто в логическом плане, а с другой стороны это нафиг не надо. Но показывать модальное окно не всегда подходит, например, кликаем по узлу дерева, надо под узлом написать "Loading..." и задизайблить большинство контролов на форме, оставив кнопу Close.
А как ты думаешь?Раз задаю такой вопрос, значит, да, думаю, что остается занятым пока сервис не отвалится по таймауту.
Но показывать модальное окно не всегда подходит, например, кликаем по узлу дерева, надо под узлом написать "Loading..." и задизайблить большинство контролов на форме, оставив кнопу Close.Это тривиально реализуется в моей схеме, но на практике будет дико и безбожно тормозить и мигать отрисовкой контролов. Зачем делать окно модальным руками мне не понятно. Для быстрых вызовов у меня есть хитрость, функционально она заключается в том, что при быстрых вызовах диалог на экране не появляется. В общем, не пытайся высасывать из пальца какие-то недостатки.
. Хотя это не обязательно, и можно написать полностью event driven GUI, но я ни разу не видел, чтобы такие реализации нормально работали при наличии сложной логики в GUI.Браузеры так работают в теории На практике и блокируются, и глючат.
Раз задаю такой вопрос, значит, да, думаю, что остается занятым пока сервис не отвалится по таймауту.По себе людей не судят.
Браузеры так работают в теории На практике и блокируются, и глючат.В браузерах всё намного проще, потому что нельзя начать вторую очередь сообщений. Все самые сложные проблемы идут от этого.
По себе людей не судят.Тебя всё же в детстве били по голове видимо, что ж ты так к людям то относишься? Где я там людей судил?
Тебя всё же в детстве били по голове видимо, что ж ты так к людям то относишься?Детка, ты опять обиделся на мой вполне нейтральный ответ? Так не суди всех разработчиков по себе, если ты забываешь или не умеешь отменять вызовы, выполняющиеся в другом потоке, то это не означает, что другие этого не умеют и/или не делают.
Детка, ты опять обиделся на мой вполне нейтральный ответ? Так не суди всех разработчиков по себе, если ты забываешь или не умеешь отменять вызовы, выполняющиеся в другом потоке, то это не означает, что другие этого не умеют и/или не делают.Лол, ты как будто там сидишь рефлексируешь и про себя пишешь . От того видимо путаешь "ты" и "я". Я задал вопрос, ты ответил вопросом на вопрос, на который я нормально ответил, в ответ получил от тебя наезд на меня лично. А в последнем сообщении у тебя вообще бред какой-то, ты там выходи уже из своей рефлексии.
Я задал вопрос, ты ответил вопросом на вопрос, на который я нормально ответил, в ответ получил от тебя наезд на меня лично.Пупсик, ты забыл упомянуть, ради чего ты задал первый вопрос. С тем же результатом можно было бы спросить, что происходит с формой модального диалога, ведь для неё надо вызывать Dispose.
P.S. А в последнем сообщении у тебя вообще бред какой-то.
Пупсикможет ты гей?
блин, опять тред скатился к сралову майка и шуреска
может ты гей?Я просто пытаюсь не задеть твоих нежных детских чувств.
блин, опять тред скатился к сралову майка и шурескаСрач получился потому, что Шурик понятия не имеет о том, как сделать простенькую реализацию RPC в обработчике GUI, но ему жутко хочется меня затроллить. При этом он умудряется слиться даже в той теме, где все меня троллят.
Вообще, мне пора заигнорить Шурика, потому что всё всегда происходит по примерно одинаковому сценарию. Автором выплёвывается какой-то пост, обычно в попытке восхвалить C# или написать, как же на самом деле хуёво устроена Java. Как только кто-то пытается копнуть поглубже, так оказывается, что автор совсем не в теме. Например, восхваляет "новую" технологию от Microsoft, которую реализовали на Java 5-10 лет назад.
На самом деле в теме есть куча интересной информации. Как тебе идея вынести её в отдельные посты?
2. Interceptor (или наш proxy код выполняемый before execution, начинает новую очередь сообщений.Вопрос: твой код работает аналогично вот этому?
static class Program
{
[STAThread]
static void Main
{
new Thread => Application.Run(new Form1("Thread2".Start;
Application.Run(new Form1("Thread1";
}
}
public class Form1 : Form
{
public Form1(string name)
{
Text = name;
KeyPress += delegate { GetAllUsers; };
}
private static void GetAllUsers
{
Thread.Sleep(1000*1000);
}
}
Результат:
Вопрос: твой код работает аналогично вот этому?Нет, лучше прочитай внимательно и задай вопросы по не понятным тебе пунктам. В моей реализации не создаётся визуального ощущения, что GUI подвисает. Потому что оно вообще не подвисает.
Нет, лучше прочитай внимательно и задай вопросы по не понятным тебе пунктам.Хорошо. Вот этот код выполняется в обработчике, который поступил в первый message loop, это так?
void UpdateUserList
{
try
{
button.Enabled = false;
userList = ServiceManager.UserService.GetAll;
}
catch (Exception e)
{
LOG.Write(e);
MessageBox.Show("Blablabla");
}
finally
{
button.Enabled = true;
}
}
И поток останавливается на строчке userList = ServiceManager.UserService.GetAll это так?
И поток останавливается на строчке userList = ServiceManager.UserService.GetAll это так?Ты прочитал тему, где я описываю вариант своего решения? Нет, поток не останавливается, он попадает в новую очередь сообщений.
как ты создаешь новый message loop?
И поток останавливается на строчке userList = ServiceManager.UserService.GetAll это так?В Windows Forms вызов form.ShowModal запускает еще один оконный цикл, так что, если внутри GetAll открывается диалоговое окно, то подвисания GUI не происходит.
В Windows Forms вызов form.ShowModal запускает еще один оконный цикл, так что, если внутри GetAll открывается диалоговое окно, то подвисания GUI не происходит.тогда всё завязано на модальный диалог, а Майк говорит, что модальный диалог не принципиален.
как ты создаешь новый message loop?Как душе угодно: можно начать его с помощью нового модального диалога или сделать руками.
while(GetMessage
{
TranslateMessage;
DispatchMessage;
}
несложно и самостоятельно, не обязательно полагаться на диалоговые окна.
несложно и самостоятельно, не обязательно полагаться на диалоговые окна.Ещё можно написать Application.DoEvents и [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern void WaitMessage но ни в том, ни в другом случае нельзя полагаться на то, что этот код постоянно выполняется и писать в цикле какую-то логику.
сделать руками.я не знаю как
while(GetMessageэто псевдокод? или реальный?
{
TranslateMessage;
DispatchMessage;
}
В общем, на первый взгляд выглядит жуткой экзотикой. Ты бы написал об этом в какой-нибудь блог и дал ссылки на международных програмерских форумах, народ бы покритиковал более компетентно чем я.
это псевдокод? или реальный?То реальный код, ещё можно сделать примерно так:
static class Program
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern void WaitMessage;
[STAThread]
static void Main
{
//Application.Run(new Form1;
var form = new Form1;
form.Show;
while (form.Visible)
{
Application.DoEvents;
if (form.Visible)
WaitMessage;
}
}
}
На самом деле погугли, тема-то популярная.
На самом деле погугли, тема-то популярная.то что ты описал пока не нагугливается
[DllImport("user32.dll", CharSet = CharSet.Auto)]че та мне это кажется подозрительным, вроде задача стандартная, поэтому думается, что стоит обходиться стандартными winform-вскими средствами
че та мне это кажется подозрительным, вроде задача стандартная, поэтому думается, что стоит обходиться стандартными winform-вскими средствамиПофигу мне на твою подозрительность, потому что если ты собрался сделать реализацию без модального диалога, то цикл сообщений, написанный вручную, будет самой незначительной твоей проблемой.
без модального диалога, то цикл сообщений, написанный вручную, будет самой незначительной твоей проблемой.вот вот, всё как-то шатко, в том плане, что надо плясать вокруг message loop-а.
Всё же обычное решение, которое в том или ином виде представлены в JQery, GWT, .NET Web Services, WCF, RIA Services, как-то более здраво смотрятся.
вот вот, всё как-то шатко, в том плане, что надо плясать вокруг message loop-а.Выдумай более реальную проблему, раз уж ты не смог понять, как добавить RPC без разламывания control flow.
Выдумай более реальную проблему, раз уж ты не смог понять, как добавить RPC без разламывания control flow.Да, я почти уверен, что если я предложу такое коллегам, они не оценят.
Да, я почти уверен, что если я предложу такое коллегам, они не оценят.Если твои коллеги такого же уровня, как ты, то может быть и не оценят.
Если твои коллеги такого же уровня, как ты, то может быть и не оценят.ага, ты вообще разочарован в IT специалистах
ага, ты вообще разочарован в IT специалистахДа, в тебе я действительно очень разочарован, если ты понимаешь, о чём я.
IEnumerable<int> UpdateUserList(IAsyncContext ctx)Думаю, теперь достаточно дописать достоинства и недостатки yield против подхода с модальным диалогом, и выделить в отдельную тему на память.
{
button.Enabled = false;
try
{
// не оборачиваю в try/catch, будем считать, что Begin* его не выкидывают до вызова End*
ServiceManager.UserService.BeginGetAll(ctx.EndDelegate);
// из try/catch yield return делать нельзя, вдруг кто-то забыл
yield return 1;
try
{
userList = ServiceManager.UserService.EndGetAll(ctx.GetAsyncResult;
}
catch (Exception e)
{
LOG.Write(e);
MessageBox.Show("Blablabla");
}
}
finally
{
button.Enabled = true;
}
}
на память.да, для потомков, которые будут писать исторические очерки о Великом и Непревзойдённом
да, для потомков, которые будут писать исторические очерки о Великом и НепревзойдённомСкажи, это тщетная попытка потроллить, подростковые претензии на юмор или влияние детских комплексов, развившихся от того, что мама пугала тебя бабаем?
глупые у тебя вопросы
глупые у тебя вопросыТы испытваешь неловкость при упоминании бабая?
Кстати, почему-то никто не написал реализацию с yield.Уфф. Здорово.
Никогда раньше не думал о возможности использовать последовательность yield return как способ увязать одно за другим асинхронные действия.
По мне так это от бедности: C# не дает нормальных средств это сделать, приходится использовать не вполне подходящие.
, посоветуй книжку по таким вот "асинхронным паттернам?" Gamma&Co читал.
IEnumerable<int> UpdateUserList(IAsyncContext ctx)А вот так как-нибудь нельзя?
{
button.Enabled = false;
try
{
// не оборачиваю в try/catch, будем считать, что Begin* его не выкидывают до вызова End*
ServiceManager.UserService.BeginGetAll(ctx.EndDelegate);
// из try/catch yield return делать нельзя, вдруг кто-то забыл
yield return 1;
try
{
userList = ServiceManager.UserService.EndGetAll(ctx.GetAsyncResult;
}
catch (Exception e)
{
LOG.Write(e);
MessageBox.Show("Blablabla");
}
}
finally
{
button.Enabled = true;
}
}
IEnumerable<int> UpdateUserList(IAsyncContext ctx)
{
button.Enabled = false;
try
{
// не оборачиваю в try/catch, будем считать, что Begin* его не выкидывают до вызова End*
var result = ctx.Execute(facade => facade.GetAll;
// из try/catch yield return делать нельзя, вдруг кто-то забыл
yield return 1;
try
{
userList = result.GetValue;
}
catch (Exception e)
{
LOG.Write(e);
MessageBox.Show("Blablabla");
}
}
finally
{
button.Enabled = true;
}
}
По мне так это от бедности: C# не дает нормальных средств это сделать, приходится использовать не вполне подходящие.т.к. вроде async/result в следующей версии C# под это будут заточены
userList = ServiceManager.UserService.EndGetAll(ctx.GetAsyncResult;Кстати, finaly мы можем писать, зато у нас появилось изменяемое состояние, мы должны следить, чтобы не вызвать EndGetAll до yield return, не приятно. В общем, без поддержки компилятора это все сыровато выглядит.
, посоветуй книжку по таким вот "асинхронным паттернам?" Gamma&Co читал.Про книжку я честно не знаю, но идея изначально принадлежит Рихтеру, он описывает её в своём Concurrent Affairs и называет AsyncEnumerator. У него есть библиотека, с помощью которой можно реализовать написанный мною код, плюс-минус. Так же его идея реализована другими людьми, но с использованием IEnumerable<Action> или других перечислений.
button.Enabled = false;
FormUtil.RemoteCall(
facade => facade.GetAll
result =>
{
if (result.Error == null)
{
userList = result.Value;
}
else
{
LOG.Write(result.Error);
MessageBox.Show("Blablabla");
}
button.Enabled = true;
});
вот здесь у тебя ошибка: пользователь может закрыть уже окно к тому времениошибка - это когда есть противоречие, а в данном случае - про такой вариант просто ничего не говорится.
если мы начинаем говорить про закрытие окна, то необходимо будет перейти к более сложной теме: взаимодействие и конфликты логических процессов.
конкретно этот конфликт может решаться на минимальном наборе правил взаимодействия:
любой процесс может быть отменен,
для самого процесса отмена выглядит, как формирование исключения,
процессы принадлежат определенному ресурсу,
процесс отменяется при "умирании" ресурса-родителя.
Метод, сформированное такими правилами простой, и достаточно предсказуемый, но есть минус, длительное (по сравнению с идеалом) время завершения: финалайзеры отрабатывают даже в том случае, когда они уже не нужны.
для уменьшения этого минуса может вводиться усложение:
отслеживается время жизни ресурсов, и те процессы, которые влияют лишь на уже "мертвые" ресурсы просто снимаются
выполнения этой последовательности пользователь работает с гуи, и может поменять состояние чего-нибудь - то есть структура кода не отражает выполняемые действияпока не понял почему это недостаток, и зачем явно указывать, что параллельно работают и другие логические процессы.
например, когда описываются sql-транзакции там же тоже это явно не описывается.
вообще, подход такой же с sql-транзакциями, если действия других логических процессов не влияют на текущий, то фиг с ними, если же влияют и приводят к конфликту, то запускается процедура разрешения конфликта(откат процесса, завершения с ошибкой и т.д.)
если синхронный вызов подвиснет на часок? Плохой инет там, например.в моем коде нет синхронных вызовов
есть лишь "синхронное" описание ожидаемого поведения кода.
из-за неучета этого момента все последующие замечания неверны
и будем его вызывать:ты хочешь чтобы messagebox вызвался последовательно после ВыполнитьЗапрос, или параллельно?
если параллельно, то значит либо ты, либо движок - должен задать правила разрешения конфликтов.
в данном случае, один из способов разрешения конфликта, указать, что MessageBox требует, чтобы кнопка была enable-нутая, и если это не так, то возникает ошибка (возможен вариант: ожидается когда она такой станет)
Я вообще не понимаю, в чём фишка делать "мегафункцию", которая и отправляет и обрабатывает. Это ж GUI. всё на событиях всё равно.если вместо одной моей строчки тебе пришлось написать 20-строк, то сколько у тебя будет строк, если необходимо описать какое-нибудь реальное поведение?..
кнопку нажал - отослался запрос
Я думал, что то, что в try выполняется много-много времени. и общем случае это может быть синхронный вызов, который нельзя остановить, который подвис(пусть даже на таймаут в 10 минут). Как мне объяснили, такие библиотеки лучше не использовать и их почти нет.
если вместо одной моей строчки тебе пришлось написать 20-строк, то сколько у тебя будет строк, если необходимо описать какое-нибудь реальное поведение?..21 в том-то и дело, что я самую общую часть почти описал. И твое решение выглядит просто вот на таком простом примере. а ассимптотически(при увеличении сложности) моё попроще.
ты хочешь чтобы messagebox вызвался последовательно после ВыполнитьЗапрос, или параллельно?Вот вот, я об этом и говорю, еще надо специфицировать "правила разрешения конфликтов". И программистам требуется держать это в голове. Это как сейчас Extract Method при наличии yield-а отличается от обычного. Например,
если параллельно, то значит либо ты, либо движок - должен задать правила разрешения конфликтов.
в данном случае, один из способов разрешения конфликта, указать, что MessageBox требует, чтобы кнопка была enable-нутая, и если это не так, то возникает ошибка (возможен вариант: ожидается когда она такой станет)
static IEnumerable<string> Method1
{
Console.WriteLine("Step1");
var result = Method2;
Console.WriteLine("Step3");
return result;
}
static IEnumerable<string> Method2
{
Console.WriteLine("Step2");
yield return "test1";
}
надо переписывать как
static IEnumerable<string> Method1
{
Console.WriteLine("Step1");
foreach (var s in Method2 yield return s;
Console.WriteLine("Step3");
}
static IEnumerable<string> Method2
{
Console.WriteLine("Step2");
yield return "test1";
}
чтобы напечаталось как хотим
Step1
Step2
Step3
Это не поломанное control flow?
А всё ради чего, чтобы finaly писать? А так обычные колбеки (с известным поведением во многих сценариях) рулят , и не надо огород городить.
Кстати, приведу вариант с моим подходом (на самом деле это аля JQuery и .т.п. подход):Этот вариант не реализует ту модель кода, которая изначально обсуждалась. Он эквивалентен следующей записи в моём случае:
button.Enabled = false;
var result = ServiceManager.UserService.GetAllWrapped;
if (result.Error == null)
userList = result.Value;
else
{
LOG.Write(result.Error);
MessageBox.Show("Blablabla");
}
button.Enabled = true;
Кроме того, в твоём коде не показано, как сменить сервис с UserService на, скажем, DocumentService.
Вот шурик код, который я смог скомпилить и получил тот же самый эффект.
А то начали с обсуждения мультитред-мультипроцесс, а сейчас уже какие-то RPC пошли, GetAll и так далее.
RPС всё-таки это c# больше вещь, я в ней не очень разбираюсь.
А можно код для интересующихся приводить?Мы обсуждаем вещи, для которых 10-20 строк кода будет недостаточно. А ты можешь положить добрую традицию и написать свой многопроцессный код?
А то начали с обсуждения мультитред-мультипроцесс, а сейчас уже какие-то RPC пошли, GetAll и так далее.В данном ответвлении обсуждается удобный способ писать неблокирующие GUI RPC.
RPС всё-таки это c# больше вещь, я в ней не очень разбираюсь.
RPC это вовсе не C#
не понял тогда пример. try мгновенно что ли выполняется?это CPS
описывается как синхронный, но реально такой код при исполнении переписывается в async или event-driven
Вот вот, я об этом и говорю, еще надо специфицировать "правила разрешения конфликтов".их в любом случае, необходимо специфицировать.
если используется ED, и несколько окон, то все равно придется как-то где-то записать, что выполнение части сообщений должно отмениться при закрытия окна.
> Это не поломанное control flow?
ты хочешь поговорим о том, как писать код, у которого есть side-эффекты?
это не ко мне. имхо, такого кода просто не должно быть. и в целом, это реально добиться
EDчто это?
Просто начали с темы, которая мне была интересна, а сейчас какие-то RPC пошли и проганье сервера. Вроде с клиента начинали.
Я представлял всё так. Есть некоторая в общем случае закрытая библиотека, в которое есть длинный синхронный вызов. Хочется сделать гуи, которая посылает запрос с помощью этой либы, обрабатывает что-то и показывает результат. При этом гуи не виснет и можно в нём что-то делать.
GUI RPC.
Ссылку хоть какую-нибудь кить, что ты под этим понимаешь. в интернетах ничего не нашёл.
Насколько я понимаю, RPC - это такое извращение, чтобы можно было не только внутри тредов к своей памяти обращаться, но и внутри нескольких процессов.
Почему это извращение - потому что человек пишет, думая, что будет быстро, а получается небыстро. Собственно я за протоколы между процессами, а не за непонятные обёртки, которые унифицируют обращение к своей и чужой памяти.
вот мой код
import multiprocessing
import os
import time
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class MainWnd(QDialog):
class STATE(object):
BASE = 1
QUERY = 2
RESULT = 3
class RESULT(object):
OK = 0
def __init__(self:
super(MainWnd, self).__init__
layout = QVBoxLayout(self)
self.button = QPushButton(self)
self.button.clicked.connect(self.clicked)
self.button.setText('send query')
layout.addWidget(self.button)
self.label = QLabel(self)
layout.addWidget(self.label)
self.result = QLabel(self)
layout.addWidget(self.result)
self.timer = QTimer(self)
self.timer.setInterval(10)
self.timer.start
self.timer.timeout.connect(self.timeout)
self.TIMEOUT = 2000 # in timer ticks
self.iter = 0
self.set_state(self.STATE.BASE)
self.subproc = None
self.pipe = None
def set_state(self, state):
self.state = state
self.show_state
def show_state(self):
self.label.setText('{}'.format(self.state
if self.state in [self.STATE.BASE]:
self.button.setDisabled(False)
else:
self.set_result('query....')
self.button.setDisabled(True)
def set_result(self,txt):
self.result.setText(txt)
def on_get_timeout(self:
self.set_result('timeout!')
self.set_state(self.STATE.BASE)
self.kill_child
def on_get_result(self,obj):
self.set_result('{}'.format(obj
self.max_iter = None
self.set_state(self.STATE.BASE)
def on_query_sent(self:
self.max_iter = self.iter + self.TIMEOUT
self.set_state(self.STATE.QUERY)
def closeEvent(self, event):
self.kill_child
def timeout(self:
self.iter += 1
if self.pipe:
if self.pipe.poll:
obj = self.pipe.recv
self.on_get_result(obj)
elif self.max_iter != None and self.max_iter < self.iter:
self.on_get_timeout
def kill_child(self:
if self.subproc:
self.subproc.terminate
self.subproc = None
self.pipe = None
self.max_iter = None
def clicked(self:
param = [0]
if not self.subproc or not self.subproc.is_alive:
self.pipe, child_conn = multiprocessing.Pipe
self.subproc = multiprocessing.Process(target = self.query_proc, args = [child_conn, param], kwargs = {} )
self.subproc.start
else:
self.pipe.send(param)
self.on_query_sent
@classmethod
def query_proc(cls, pipe,param):
import select
while 1:
ret = cls.query(param)
pipe.sendcls.RESULT.OK, ret
if not(pipe.poll(10:
return
param = pipe.recv
@classmethod
def query(cls,params):
import random
result = random.randrange(1,30)
time.sleep(result)
return result
def proc_test:
app = QApplication(sys.argv)
mainwnd = MainWnd
mainwnd.show
app.exec_
if __name__ == '__main__':
proc_test
RPC- remote procedure call? где кроме винды она используется?
import xmlrpclib
Тогда беру слова про RPC обратно. Ничего ужасного.
И твое решение выглядит просто вот на таком простом примере. а ассимптотически(при увеличении сложности) моё попроще.давай возьмем сложный пример. например, копирование файлов
using (new Transaction //копирование делается в транзакции, если происходит ошибка, то все копирования отменяются
{
var files = gui.SelectedFiles;
var targetDir = gui.TargetDirectory;
foreach (var file in files)
{
try
{
file.CopyTo(targetDir); //исполняться будет асинхронный вариант
}
catch (IOException exc)
{
var targetFilename = GetTargetFilename(file, targetDir);
if (File.Exists (targetFilename
{
if (gui.Question("Файл уже существует. Переписать?", Answer.Yes | Answer.No) == Answer.Yes) //асинхронный запрос пользователя
{
file.CopyTo(TargetFilename:targetFilename); //исполняться будет асинхронный вариант
}
}
else
throw;
}
}
}
как это будет выглядеть у тебя?
что это?event-driven
Кроме того, в твоём коде не показано, как сменить сервис с UserService на, скажем, DocumentService.надо всего лишь передать IUserService как generic параметр.
Можно так:
RemoteCaller<IUserService>.Call(service => ...);
можно так:
RemoteCaller.Create<IUserService>.Call(service => ...);
такого кода просто не должно бытьне понял, у тебя нигде нет side эффектов? а, например, с базой как работаешь?
зачем using, если у на side эффектов нет ?
Написал синхронный код со словами: "А давай перепиши его в ассинхронный, который будет проще". А у меня мой синхронный код, будет исполняться параллельно благодаря суперкомпилятору.
Я ж не спорю, что руками будет сложнее.
Давай реализацию Transaction включим в твой пример и прочих ассинхронных вариантов и сравним.
вопрос-то о чём был: что сложнее, процессами или тредами. Я у тебя тредов не вижу. Это ты считаешь, что там треды, а вдруг там разработчики тесты провели и оказалось, что процессами круче и у них там в дочернем процессе исполяется.
И твое решение выглядит просто вот на таком простом примере. а ассимптотически(при увеличении сложности) моё попроще.
Это относилось к тому, что если всё синхронно писать и будем много разных событий и по каждому будет запускаться отдельный тред и что-то синхронно делать, то централизованно хранить состояния и обрабатывать события проще.
Ты же сейчас говоришь, что всё это где-то будет исполняться прозрачно от реализации вообще. Без тредов, без процессов и ассинхронности.
А если пишешь синхронно и понятно, а исполняется асинхронно - то это супер. Я вообще не знал, что так можно.
не понял, у тебя нигде нет side эффектов? а, например, с базой как работаешь?изменение данных - не side-эффект, а явное изменение состояния, удовлетворяющее куче соглашений
код драйвера transacted NTFS тоже включить?
Это ты считаешь, что там треды, а вдруг там разработчики тесты провели и оказалось, что процессами круче и у них там в дочернем процессе исполяется.я так не считаю.
данный код можно запустить и через очередь сообщений, и через треды(или тредпул можно и через процессы, в зависимости от написанного провайдера, которому этот код передается на исполнение.
> вопрос-то о чём был: что сложнее, процессами или тредами.
в данной подветке, скорее от вопроса "как лучше исполнять - тредами или процессами?" перешли к вопросу "как лучше описывать?"
ps
по поводу тредов и процессов, вроде же определились в плюсах и минусах:
треды будут быстрее, если есть большое состояние в памяти которое надо менять
процессы, по умолчанию, лучше масштабируется (но и на тредах, если руководствоваться несколькими правилами, это также можно обеспечить; на процессах таких правил меньше)
изменение данных - не side-эффект, а явное изменение состояния, удовлетворяющее куче соглашенийесли принять такое твое извращенное понятия side эффекта, тогда не понятно, что криминального ты увидел в моем коде?
там всего лишь "явное изменение состояния"
там всего лишь "явное изменение состояния"ты смешал два разных подхода - у тебя yield-ы используются: и для описания асинхронного кода, и для описания итератора данных - и у тебя ожидаемо получилась хрень.
если так не делать, то и хрени не будет. на это даже можно создать правило fxcop
в данной подветке, скорее от вопроса "как лучше исполнять - тредами или процессами?" перешли к вопросу "как лучше описывать?"
Да вроде очевидно, что чем проще написано - тем круче.
Я просто считал (да и продолжаю наверно что в теории все эти красивости очень вкусные, а когда дело доходит до реализации, то какая-нибудь жопа вылезает.
Потому как мне плохо понятно, как можно в одном ED обработчике можно предусмотреть всё, что будут использовать. Ведь здесь идея-то такая. Пишите, как вам проще и самую суть, а мы исполним, как будет лучше и быстрее.
Всё равно придётся свой new Transaction писать или старый расширять. А там-то и придётся реализовывать всё.
Хотя опять же, тему знаю... чуть поболе, чем совсем не знаю. Поэтому может мои взгляды и устарели.
code:Вот пробная реализация using сценария :
пользователь нажал кнопку
using(показывать-что-команда-выполняется
{
экран.определенная-область = cервер.выполнить-запрос;
}
Executor.Execute(
facade => facade.Method2
resultHandler => new Disposable("Disposable1").Using(
disposable1 => new Disposable("Disposable2").Using(
disposable2 => resultHandler.Create(
result => Console.WriteLine(result
exception => Console.WriteLine(exception;
Вот пробная реализация using сценария :имхо, единственный плюс, что такая запись держится начиная с c# 3.0
Оставить комментарий
Marinavo_0507
Ну типа например GUI взаимодействует с бизнес-логикой через IPC. Если завтра захочется веб-интерфейс или там мобильный клиент, то надо переписать только GUI.Никаких потоков, GUI работает по принципу отправил запрос - подождал - получил ответ.