Как описывать действия над gui? (ED, cps, async, yield) [re: Я раз]

Marinavo_0507

Из простых примеров могу предложить любой десктоп с неблокирующимся во время вызовов на сервер GUI. Я вообще слабо представляю, зачем в этом случае использовать многопроцессное решение, если достаточно двух потоков.
Ну типа например GUI взаимодействует с бизнес-логикой через IPC. Если завтра захочется веб-интерфейс или там мобильный клиент, то надо переписать только GUI.
Никаких потоков, GUI работает по принципу отправил запрос - подождал - получил ответ.

kokoc88

Ну типа например GUI взаимодействует с бизнес-логикой через IPC. Если завтра захочется веб-интерфейс или там мобильный клиент, то надо переписать только GUI.
Мы и говорим о примере, когда GUI взаимодействует с бизнес логикой через IPC.
Никаких потоков, GUI работает по принципу отправил запрос - подождал - получил ответ.
Пока GUI ждёт ответ, он не должен блокироваться; и ещё было бы неплохо нарисовать какой-нибудь progress bar.

Marinavo_0507

Пока GUI ждёт ответ, он не должен блокироваться; и ещё было бы неплохо нарисовать какой-нибудь progress bar.
ну и какая проблема, GUI же делают на основе message loop
пришёл ответ от базы - сообщение доходит обычным образом, а прогресс бар рисуется по сообщениям от таймера

kokoc88

ну и какая проблема, GUI же делают на основе message loop
пришёл ответ от базы - сообщение доходит обычным образом, а прогресс бар рисуется по сообщениям от таймера
Проблем несколько. Например, мы обсуждали возможность небольшой обработки данных после ответа от сервера, которая занимает несколько секунд. Да и получить ответ от сервера сразу в message loop не всегда так просто, да и не всегда возможно.

Dasar

Да и получить ответ от базы в message loop не всегда так просто, да и не всегда возможно.
возможно, для этого достаточно однажды написать библиотечную функцию, которая перекидывает ответ в виде gui-сообщения, аля:

void DbExecute(this Control control, string query, Action onexecuteaction)
{
AsyncDbExecute(query, =>control.BeginInvoke(onexecuteaction;
}

ps
код действий правда не очень красивый получается, ломается блочная логика (не получается нативно использовать деструкторы, try/catch, using, for и т.д.)
но это решается, если в языке есть cps

kokoc88

возможно, для этого достаточно однажды написать библиотечную функцию, которая перекидывает ответ в виде gui-сообщения, аля:
Возможно, ещё для этого подходит паттерн State, и я подобное решение опробовал на практике. В итоге код было очень сложно поддерживать. На C# я подхватил идею Рихтера и стал использовать yiled.

Dasar

Ну типа например GUI взаимодействует с бизнес-логикой через IPC.
минусы:
1. размазывается логика по куче слоев (любую долгую хреньку необходимо делать в виде отдельного агента)
2. необходимо постоянно держать в голове - это быстрое действие, или это долгое действие? причем, напрямую, по коду это не видно
3. вместо нативных конструкций (for, using, try, вызов функции и т.д.) приходится городить их аналоги
4. необходимо писать доп. код, по формированию и обработке сообщений

Dasar

GUI взаимодействует с бизнес-логикой через IPC. Если завтра захочется веб-интерфейс или там мобильный клиент, то надо переписать только GUI.
и это, кстати, приводит к утверждению, которое противоречит идеям бизнеса(рубль завтра намного дешевле, чем рубль сегодня, как минимум на двадцать годовых): давайте напишем сегодня в несколько раз сложнее(давайте сегодня потратим сегодня в несколько раз больше денег и это может быть поможет потратить денег меньше когда-нибудь.
хочется-то, при той же сравнимой сложности(а лучше еще и сильно упростить) сделать так, чтобы легко было переделывать.

Marinavo_0507

ну это понятно
поэтому все делают GUI, которое подвисает - так дешевле всего

6yrop

минусы:
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;
}
}

6yrop

т.е. под ReSharper-ом такой код сопровождается без какого-либо заметного оверхеда

6yrop

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;
}

Оба метода легко выражаются друг через друга.

Dasar

большинство из этих пунктов уходят, если предложение -а оформить вот в такую конструкцию (реализация через Castle.DynamicProxy):
обоснуй, пожалуйста

Marinavo_0507

я честно говоря ничего не понял
это CPS что ли?
а в каком месте там получается другой тред?

zorin29

Другой тред там не написал, он зашит в реализацию Execute. Идяе -а в том, что единственное место, где явно присутствуют треды в коде - это реализация Execute.

6yrop

это CPS что ли?
насколько я понимаю, да
а в каком месте там получается другой тред?

С помощью Castle.DynamicProxy можно сделать динамическую реализацию (proxy) интерфейса IFacade. Этот прокси перенаправляет вызовы всех методов в одну точку:

namespace Castle.DynamicProxy
{
public interface IInterceptor
{
void Intercept(IInvocation invocation);
}
}

Мы подсовываем свою реализацию IInterceptor, в которой и происходит переброска в другой поток и обратно.

Dasar

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

Marinavo_0507

твои недостатки тоже не понял, кроме №3
да и он не недостаток, если приложение уже event-driven

pilot

как, например, в таком коде выглядит обработка ошибок? и насколько она отличается от нативной, через обработку исключений?
Такими темпами вы скоро веб-интерфейсы изобретете! :ooo:

6yrop

как, например, в таком коде выглядит обработка ошибок? и насколько она отличается от нативной, через обработку исключений?
обработка ошибок в onErrorAction.
Тебе хочется оборачивать в один try куски из UI кода и куски не UI кода? А нафига это надо?

6yrop

Такими темпами вы скоро веб-интерфейсы изобретете!
протокол да близок, интерфейсы нет, омерзительный js нам не нужен.

pilot

протокол да близок, интерфейсы нет, омерзительный js нам не нужен.
Ну я имел в виду что вы отстали от жизни лет на 10 :grin:
JS конечно проблема, но уж что выросло то выросло :(

6yrop

Ну я имел в виду что вы отстали от жизни лет на 10
отстали от чего?

Dasar

Тебе хочется оборачивать в один try куски из UI кода и куски не UI кода? А нафига это надо?
я хочу, чтобы один поток управления был оформлен, как один поток кода, что позволит использовать нативные средства кода для организации потока управления (foreach, try/using, вызов функций)
если поток управления затрагивает и gui, и не-gui, то да try будет затрагивать и gui, и не gui
зы
использование нативных средств достигает две цели:
1. используются лучшие инструменты для описания (если нативные средства не лучшие, тогда значит нативные средства устарели и их необходимо заменить)
2. уменьшает стоимость сопровождение кода в два раза (нет необходимости поддерживать два разных набора конструкций для описания одного и того же

Dasar

JS конечно проблема, но уж что выросло то выросло :(
для того, чтобы в продукте использовался js, не обязательно кодить на js

Dasar

твои недостатки тоже не понял, кроме №3
если вспомнить часть критериев хорошего кода, то видно, почему это недостатки:
писать код так, чтобы требовалось как можно меньше информации для того, чтобы представить как код будет себя вести
а. использовать как можно меньший "алфавит" (как можно меньше требуется усвоить дополнительной информации перед прочтением кода)
b. при чтении локального куска кода, как можно меньше информации требуется из других частей кода
да и он не недостаток, если приложение уже event-driven
оформление кода, как event-driven само со себе очень большой недостаток, не сильно лучше, чем многопоточность.
представить при этом по коду с минимум доп. информации, что будет реально происходить, и там, и там почти невозможно.
при этом исполнение, как event-driven - полезно.

pilot

для того, чтобы в продукте использовался js, не обязательно кодить на js
Спасибо, капитан!

Dasar

Спасибо, капитан!
да, не за что, коллега

6yrop

1. используются лучшие инструменты для описания (если нативные средства не лучшие, тогда значит нативные средства устарели и их необходимо заменить)
"лучшие инструменты для описания" сильно размытый и спорный термин
уменьшает стоимость сопровождение кода в два раза (нет необходимости поддерживать два разных набора конструкций для описания одного и того же

GUI код описывает свои задачи, не GUI код свои. Где одно и тоже два раза?

Dasar

"лучшие инструменты для описания" сильно размытый и спорный термин
можно ввести более-менее однозначный критерий для поиска лучшего инструмента: чем меньше информации для описания требуется, тем лучше.
да, такой критерий требует поправку на учет ограничений мышления человека (ограниченные способности для запоминания, распознавания и т.д. но критерий все равно при этом останется однозначным

Dasar

GUI код описывает свои задачи, не GUI код свои. Где одно и тоже два раза?
я говорил про два набора инструментов.
если для обработки ошибок есть инструмент try/catch, то зачем нужна какая-то другая конструкция, которая делает то же самое (обеспечивает передачу управления на спец. кусок кода при ошибке)?
если для описания последовательности действий используется последовательная запись операторов, то зачем нужна какая-то другая конструкция?

Marinavo_0507

если для описания последовательности действий используется последовательная запись операторов, то зачем нужна какая-то другая конструкция?
ну gui так никто не пишет, потому что последовательность действий там определяет в основном пользователь

6yrop

я говорил про два набора инструментов.
где удвоение стоимости разработки? У тебя выходит, если есть два инструмента, то работа стоит в два раза дороже. Хрень какая-то.

6yrop

обеспечивает передачу управления на спец. кусок кода при ошибке
никто хорошо не определит термин "ошибка", поэтому это не подходит в качестве формулировки требований.
если для обработки ошибок есть инструмент try/catch

да, в языке есть конструкция try/catch. Она работает так то и так то, одной важной особенностью является то, что она раскручивает стек, прерывая выполнения функций. Да, есть ситуации, когда это полезно. Но нужно ли это при организации схемы запрос/ответ? Такой потребности пока не видно. Зачем может потребоваться раскручивать стек со смесью GUI и не GUI кода?

Dasar

ну gui так никто не пишет, потому что последовательность действий там определяет в основном пользователь
до этого речь шла про один логический поток управления, а не про взаимодействие множества логических потоков управления.

Marinavo_0507

до этого речь шла про один логический поток управления,
нет, речь шла о том, чтобы в GUI можно было что-то делать, пока где-то в каком-то другом потоке работает запрос

Dasar

нет, речь шла о том, чтобы в GUI можно было что-то делать, пока где-то в каком-то другом потоке работает запрос
и какой логической связкой это утверждение отрицает предыдущее утверждение?

doublemother

Такими темпами вы скоро веб-интерфейсы изобретете! :ooo:
Следующим этапом будет клиент-серверная архитектура, как таковая, и гуи, работающие через dbus.

Dasar

Но нужно ли это при организации схемы запрос/ответ? Такой потребности пока не видно. Зачем может потребоваться раскручивать стек со смесью GUI и не GUI кода?
один из типичных сценариев gui (при этом эта та самая смесь gui и не-gui в едином потоке управления):
пользователь нажал кнопку
 вывелось что команда начала выполняться (например, за-disable-илась кнопка)
 запрос отправился на сервер
 выводится процесс выполнения (например, бегает прогресс-бар или часы)
 сервер прислал ответ, что запрос выполнился (успешно или неуспешно)
 вывелся результат
 (*)"очистилось" отображение, что команда выполняется, и отображение процесса выполнения (кнопка за-enable-илась обратно, прогресс-бар(часы) убрались)
при этом пункт со звездочкой должен выполниться гарантированно, в независимости от того, последовательность выполнилась как планировалось, или что-то пошло не так(пропал сервер, кончилась память(или диск) на клиенте при попытке сформировать запрос(обработать запрос) и т.д.)
и это есть стандартный паттерн захвата/очистки ресурса, который нативно оформляется, или через try/finally, или через using/destructor
отправка сообщения на сервер с последующим получением и выводом результата - это стандартный паттерн вызова функции(или метода)
это дает код вида:

пользователь нажал кнопку
вывелось что команда начала выполняться (например, за-disable-илась кнопка)
try
{
запрос отправился на сервер
выводится процесс выполнения
сервер прислал ответ, что запрос выполнился (успешно или неуспешно)
вывелся результат запроса
}
finally
{
(*)"очистилось" отображение [..]
}


или если использовать все перечисленные свертки:

пользователь нажал кнопку
using(показывать-что-команда-выполняется
{
экран.определенная-область = cервер.выполнить-запрос;
}

этот же сценарий можно описать тысячью разными способами: через event-driven, через асинхронные вызовы делегатов и т.д., но в них будет расти уровень "шума": будет расти доля слов, которые ничего полезного не описывают с точки зрения логической последовательности работы сценария
зы
во многих приложениях видно, что паттерн try/finally часто игнорируют в таких сценариях, и кнопки обратно не отжимаются, если что-то идет не так как планировалось (например, при временной пропаже соединения с сервером)
 

Marinavo_0507

при этом пункт со звездочкой должен выполниться гарантированно, в независимости от того, последовательность выполнилась как планировалось, или что-то пошло не так(пропал сервер, кончилась память(или диск) на клиенте при попытке сформировать запрос(обработать запрос) и т.д.)
вот здесь у тебя ошибка: пользователь может закрыть уже окно к тому времени
так как окна, которые нельзя закрыть, пока не выполнится какая-то длинная операция - жутко бесят
ну и ещё недостаток - в твоём коде нигде не указано, что во время
выполнения этой последовательности пользователь работает с гуи, и может поменять состояние чего-нибудь - то есть структура кода не отражает выполняемые действия

6yrop

Кстати, async/await можно будет использовать внутри лямбд?

6yrop

отправка сообщения на сервер с последующим получением и выводом результата - это стандартный паттерн вызова функции(или метода)
вызов функции это вызов функции: делаем вызов, ждем результата, получаем результат, т.е. обычная синхронность. Полностью избавиться от различий между синхронным и асинхронным вызовом нельзя, это видно даже из общих соображений без привязки к какому-либо инструменту.

Phoenix

 
это дает код вида:

если синхронный вызов подвиснет на часок? Плохой инет там, например.
А то память кончиться может, а инет сдохнуть(или серверная часть синхронного вызова) не может? Как-то однобоко получается.
Тогда этот finaly не выполнится вообще. Да ещё и прогу придётся килить, а это может означать недописанные файлы, например.
Приходишь вот так в бухгалтерию, чтобы бумагу забрать, а они тебе "а у нас тут всё висит, нажимаешь кнопку, и ничего не проиходит". и сидишь ждёшь, а кильнуть нельзя - локальная база какая-нибудь может сломаться и потом нужно будет неделю ждать специалиста, чтобы он её разлочил.
где тут шум?
Я вообще не понимаю, в чём фишка делать "мегафункцию", которая и отправляет и обрабатывает. Это ж GUI. всё на событиях всё равно.
кнопку нажал - отослался запрос
пришёл ответ - вывели результат
 

пользователь нажал кнопку
try
{
создался дочерний процесс(принят запрос)
отправилось сообщение процессу
сменить состояние(запрос отправлен)
}
catch
{
сообщение о неудаче
сменить состояние(ожидание команды)
}

пользователь нажал кнопку "отменить" (или закрыл окно)
отсылается сообщение процессу помереть.
ждём 100мс
убиваем процесс
сменить состояние(ожидание команды)

процесс2-main
запрос отправился на сервер
сервер прислал ответ, что запрос выполнился (успешно или неуспешно)
отослался результат основному процессу

пришёл ответ
вывелся результат запроса
сменить состояние(ожидание команды)

сменить состояние(NEWSTATE)
STATE = NEWSTATE
отобразить состояние
отобразить состояние
если STATE == принят запрос
вывелось что команда начала выполняться (например, за-disable-илась кнопка)
если STATE == запрос отправлен
вывелось что команда начала выполняться2 (например, за-disable-илась кнопка)
если STATE == ожидание команды
"очистилось" отображение [..]

6yrop

Оформим твой код в метод:
void ВыполнитьЗапрос
{
пользователь нажал кнопку
вывелось что команда начала выполняться (например, за-disable-илась кнопка)
try
{
запрос отправился на сервер
выводится процесс выполнения
сервер прислал ответ, что запрос выполнился (успешно или неуспешно)
вывелся результат запроса
}
finally
{
(*)"очистилось" отображение [..]
}
}

и будем его вызывать:

void Main
{
ВыполнитьЗапрос;
MessageBox.Show("Предлагаем пользователю нажать кнопку.") //а она задизейблена
}

Это я к тому, что такой код становиться необычным, его нельзя выделять в метод и ожидать обычное поведение.

kokoc88

вот здесь у тебя ошибка: пользователь может закрыть уже окно к тому времени
так как окна, которые нельзя закрыть, пока не выполнится какая-то длинная операция - жутко бесят
Код модельный, понятное дело, что в реальном приложении всё будет намного сложнее. Кроме/вместо задизейбленной кнопки будет выводиться какой-нибудь диалог с кнопкой отмены или что-то в этом роде. Чем сложнее решаемая задача и чем больше состояний, тем хуже будет выглядеть код, в котором ломается control flow.
ну и ещё недостаток - в твоём коде нигде не указано, что во время
выполнения этой последовательности пользователь работает с гуи, и может поменять состояние чего-нибудь - то есть структура кода не отражает выполняемые действия
Обычно запросы простые и этого не нужно, главное, чтобы GUI не создавало у людей ощущения, что программа повисла. То есть достаточно отрисовки, анимации какого-нибудь progress bar и кнопки отмены.

kokoc88

если синхронный вызов подвиснет на часок? Плохой инет там, например.
Use timeouts, Luke.
Я вообще не понимаю, в чём фишка делать "мегафункцию", которая и отправляет и обрабатывает. Это ж GUI. всё на событиях всё равно.
кнопку нажал - отослался запрос
пришёл ответ - вывели результат

Чего ты точно не понимаешь, так это сложности разработки программы при наличии этих состояний. О чём пишет : при выполнении запроса на сервер секции try/catch/finally тебе придётся разбить на три отдельных блока, при этом ломается и становится на порядки сложнее control flow.

Marinavo_0507

То есть достаточно отрисовки, анимации какого-нибудь progress bar и кнопки отмены.
Ну то есть окно ничего не делает, занимает экран (а то и фокус, если модальное только нагло показывает анимацию. Хочется взять и уебать этих ИТ-специалистов и их менеджера.
Ну это тогда же от платформы зависит - как там проще всего преобразовать синхронный вызов в асинхронный. Если надо руками создавать треды - ну значит платформа несколько недоделанная, надо добавить эту свою библиотечную функцию и снова забыть про треды. Но помнить про реентерабельность.

kokoc88

Ну то есть окно ничего не делает, занимает экран (а то и фокус, если модальное только нагло показывает анимацию. Хочется взять и уебать этих ИТ-специалистов и их менеджера.
Если речь идёт о запросах, которые выполняются более 10 секунд, то да, GUI нужно проектировать так, чтобы можно было работать с программой, пока выполняется долгий запрос. Если речь идёт о быстрых запросах, то лучше блокировать весь GUI (конечно же нарисовав кнопку отмены потому что иначе разработчики наделают такое количество ошибок, что закачаешься. Я на практике опробовал кучу методов, видел во что они превращаются при наращивании функционала, и теперь стараюсь придерживаться простого сценария.
Ну это тогда же от платформы зависит - как там проще всего преобразовать синхронный вызов в асинхронный. Если надо руками создавать треды - ну значит платформа несколько недоделанная, надо добавить эту свою библиотечную функцию и снова забыть про треды. Но помнить про реентерабельность.
Если рассматривать любую библиотеку общего назначения, то в ней, скорее всего, не будет возможности получать сообщения прямо в GUI. Потому что GUI библиотеки могут быть любыми, никто не будет выпускать библиотеку для HTTP запросов отдельно для Windows Forms и отдельно для WPF.
Если вести речь про .NET, то синхронный вызов в асинхронный переделать очень просто. И потоки руками создавать не придётся, но придётся про них помнить. То есть в этом случае мы не уходим от сценария, когда разработчик должен знать, как это работает.

Phoenix

Use timeouts, Luke.

Синхронный вызов может быть реализован в закрытой библиотеке, которая свои таймауты устанавливает. И потом, можно не все учесть, может быть(или появиться) баг в библиотеке.
Второе: это можно быть супер крутой запрос в БД, который длится 2-3минуты. таймаут в этом случае будет минут 10-20, так?
Во время выполнения запроса, сисадмин Василий, решил перенастроить роутинг. Когда ему позвонили спросить, что "ничего не работает", он предложил перезапустить приложение.
Я один раз где-то вот минут 10 стоял около банкомата, ждал пока он созреет. Бабло вот списалось, а денег не дали.(это не аргумент против тредов, это намёк, что такие таймауты реальны вполне)
Я опаздывал.
Я не прочь был бы нажать "не надо денег, отдай карточку, я опаздываю". Тогда бы мне не пришлось ехать в банк и писать заявление, чтобы денеги вернули.
Чего ты точно не понимаешь, так это сложности разработки программы при наличии этих состояний

понимаю, что это немного сложнее в данном случае. В общем случае это значительно сложнее. Но в общем случае, либо задача разбивается на несколько независимых состояний, тогда сводится к более простой, либо без централизованной обработки состояний её решить намного проще.
при выполнении запроса на сервер секции try/catch/finally тебе придётся разбить на три отдельных блока, при этом ломается и становится на порядки сложнее control flow.

От того, что все 3 блока внутри одного try, меньше ломаться не станет.
была поставлена задача: что бы ни произошло, должен выполниться finaly. Я привёл пример, когда синхронный вызов подвисает. В этом случае finaly никогда не выполнится.
Или мы предполагаем, что синхронный вызов подвиснуть не может ни при каких обстоятельствах?

Phoenix

Если речь идёт о быстрых запросах, то лучше блокировать весь GUI

...например, выполняя запрос в GUI-треде. :grin:

Marinavo_0507

Если рассматривать любую библиотеку общего назначения, то в ней, скорее всего, не будет возможности получать сообщения прямо в GUI. Потому что GUI библиотеки могут быть любыми, никто не будет выпускать библиотеку для HTTP запросов отдельно для Windows Forms и отдельно для WPF.
Есть же системные средства. Обычные сообщения Windows, которые WM_что-то. Для *nix такие библиотеки интегрируются в select loop, в него же идут сообщения от X11. Своей обёрткой это наверное придётся обернуть, но один раз.
Если вести речь про .NET, то синхронный вызов в асинхронный переделать очень просто. И потоки руками создавать не придётся, но придётся про них помнить. То есть в этом случае мы не уходим от сценария, когда разработчик должен знать, как это работает.
Поясни плиз суть проблемы, не могу себе представить, зачем помнить про реализацию.

kokoc88

Синхронный вызов может быть реализован в закрытой библиотеке, которая свои таймауты устанавливает. И потом, можно не все учесть, может быть(или появиться) баг в библиотеке.
В этом случае отдельный процесс и ломаный control flow тебе ничем не поможет.
Второе: это можно быть супер крутой запрос в БД, который длится 2-3минуты. таймаут в этом случае будет минут 10-20, так?
Нет, читай выше ответ -у.
Но в общем случае, либо задача разбивается на несколько независимых состояний, тогда сводится к более простой, либо без централизованной обработки состояний её решить намного проще.
Выше я уже писал про паттерн State. Да, это работает, но удобно и легко поддерживается только в частных случаях. Очень часто связи между состояниями неявные, один вполне линейный процесс приходится собирать по кубикам в разных местах или в разных файлах. Переходы между состояниями тоже бывают неявными, иногда тяжело понять, по какому пути пойдёт выполнение.
От того, что все 3 блока внутри одного try, меньше ломаться не станет.
Если все три блока внутри одной линейной конструкции, то control flow у тебя перед глазами. Если они разнесены на три разные функции, то код читать намного тяжелее.
была поставлена задача: что бы ни произошло, должен выполниться finaly. Я привёл пример, когда синхронный вызов подвисает. В этом случае finaly никогда не выполнится.
Если вызов подвисает, то тебе не поможет ни одна модель. (Хотя лично я не видел библиотек, которые что-то вызывают по сети и не поддерживают отмены, а если увижу, то не буду использовать.)

kokoc88

Есть же системные средства. Обычные сообщения Windows, которые WM_что-то. Для *nix такие библиотеки интегрируются в select loop, в него же идут сообщения от X11. Своей обёрткой это наверное придётся обернуть, но один раз.
Системные средства доступны на самом низком уровне, под .NET пользоваться очередью сообщений не удобно, можешь получить ещё больше проблем, чем при создании отдельного потока. А если не умеешь этого делать, то и на Си++ будут ошибки: поищи темы про "когда я двигаю окно, у меня перестаёт доставляться сообщение WM_TIMER", за последний год-два здесь была пара таких сообщений.
По этой и вышеназванным причинам в библиотеку для HTTP вызовов вряд ли будут интегрировать очередь GUI сообщений.
--------------------------------------------------------------------------------
Если вести речь про .NET, то синхронный вызов в асинхронный переделать очень просто. И потоки руками создавать не придётся, но придётся про них помнить. То есть в этом случае мы не уходим от сценария, когда разработчик должен знать, как это работает.
--------------------------------------------------------------------------------
Поясни плиз суть проблемы, не могу себе представить, зачем помнить про реализацию.
Не про реализацию, а про то, как работает. Асинхронный вызов будет выполняться в отдельном потоке, поэтому необходимо использовать мьютексы, очередь сообщений для передачи данных в GUI, и так далее.

Phoenix

Второе: это можно быть супер крутой запрос в БД, который длится 2-3минуты. таймаут в этом случае будет минут 10-20, так?

Нет, читай выше ответ -у.

я хочу отменить запрос, а не продолжать работать с программой.
Если вызов подвисает, то тебе не поможет ни одна модель. (Хотя лично я не видел библиотек, которые что-то вызывают по сети и не поддерживают отмены, а если увижу, то не буду использовать.)

Как ты себе это представляешь? Запускается дополнительный тред(или в основном в котором вызывается cancel_last_query и синхронный вызов(который в соседнем треде идёт) завершается с ошибкой ERR_USER_CANCELED?

kokoc88

я хочу отменить запрос, а не продолжать работать с программой.
Нажимаешь кнопку "Отмена". В случае отдельного потока в .NET можно просто закрыть сетевой ресурс прямо из GUI потока.
Как ты себе это представляешь? Запускается дополнительный тред(или в основном в котором вызывается cancel_last_query и синхронный вызов(который в соседнем треде идёт) завершается с ошибкой ERR_USER_CANCELED?
Найди тему, где я объяснял green-у, как может быть реализована отмена операции, которая идёт в другом потоке. Можно, как я написал выше, просто проставить флаг и закрыть сокет, получить исключение во втором потоке, отобразить его в GUI.

Phoenix

Можно, как я написал выше, просто проставить флаг и закрыть сокет, получить исключение во втором потоке, отобразить его в GUI

Жестоко. Но всё-таки сужается множество возможных библиотек для использования до тех которые не подвисают или имеют средства аварийно останавливаться пользователем.

Marinavo_0507

Асинхронный вызов будет выполняться в отдельном потоке, поэтому необходимо использовать мьютексы, очередь сообщений для передачи данных в GUI, и так далее.
Ну вызов получил параметры, отослал их серверу, ждёт-ждёт-ждёт, получает ответ с результатом, отсылает GUI сообщение со ссылкой на результат. Мутексов никаких, очередь - она та же самая, что у всего остального GUI. Ну да, надо помнить, что нельзя просто так результат зафигачить в глобальную переменную (типа взять главное окно, и в нём поле) - надо дать ссылку на него GUI-треду, пусть разбирается - ты про это?

kokoc88

От того, что все 3 блока внутри одного try, меньше ломаться не станет.
Есть ещё одна хитрость: можно сделать асинхронные вызовы, запуская очередь GUI сообщений для ожидания ответа от сервера. Тогда control flow остаётся максимально прозрачным. Вот каким может быть пример неблокирующего вызова из GUI на Windows Forms: userList = ServiceManager.UserService.GetAll Логика работы весьма простая: во время вызова GetAll отображается модальный диалог "Идёт запрос на сервер...", бесконечный прогресс бар или другая анимация, и кнопка "Отмена", исключения перевыкидываются вызывающему потоку. При этом запрос выполняется в другом потоке, а у пользователя не создаётся ощущения, что программа подвисла. Для того, чтобы сделать такую механику, нужны базовые знания по многопоточному программированию.
Если метод GetAll должен провести какую-то обработку данных, про которую мы рассуждали в нашем примере, то во время выполнения этой обработки внутри реализации GetAll нужно будет помнить, что доступ к данным идёт из другого потока.

kokoc88

Жестоко. Но всё-таки сужается множество возможных библиотек для использования до тех которые не подвисают или имеют средства аварийно останавливаться пользователем.
Множество библиотек, которые я использую последние пять-шесть лет, при этом не сокращается. Таймаут или отмену поддерживает вообще всё, что имеет мало мальскую значимость в closed/open source.

kokoc88

Ну вызов получил параметры, отослал их серверу, ждёт-ждёт-ждёт, получает ответ с результатом, отсылает GUI сообщение со ссылкой на результат. Мутексов никаких, очередь - она та же самая, что у всего остального GUI. Ну да, надо помнить, что нельзя просто так результат зафигачить в глобальную переменную (типа взять главное окно, и в нём поле) - надо дать ссылку на него GUI-треду, пусть разбирается - ты про это?
Да, примерно про это.

Marinavo_0507

ну значит надо, чтоб твоя обёртка, которая соединяет асинхронные вызовы с сообщениями GUI, делала ровно это - и тогда забыть про то, что внутри

Phoenix

посмотри мой пример. там все три блока идут подряд. Никуда лазить не нужно.
Правда там обработки нет. Но её можно прямо в том же процессе и делать, отослав основному процессу сообщение: "данные получены, идёт обработка".
Я всё равно не понимаю, в чём такая ценность того, что 3 строки написаны внутри одного try. При чём друг с другом совсем не связаны в общем-то.

Marinavo_0507

поищи темы про "когда я двигаю окно, у меня перестаёт доставляться сообщение WM_TIMER", за последний год-два здесь была пара таких сообщений.
нашёл
это аргумент в пользу системных средств - стандартный message loop внутри winapi обработает всё правильно

kokoc88

ну значит надо, чтоб твоя обёртка, которая соединяет асинхронные вызовы с сообщениями GUI, делала ровно это - и тогда забыть про то, что внутри
Да, выше я уже описал красивую механику для этого. И я продолжаю считать, что профессиональный разработчик, получающий сегодня ~120к на руки, должен понимать, как это работает; должен уметь написать реализацию такой фигни с нуля; и должен быть способен исправить ошибку, которую его коллега может сделать в этой библиотеке.

kokoc88

нашёл
это аргумент в пользу системных средств - стандартный message loop внутри winapi обработает всё правильно
Это аргумент в пользу чтения документации перед тем, как что-то делать. Если мне нужно использовать WM_USER + X, то я делаю это правильно, и моя программа продолжает работать, когда кто-то двигает окно.

kokoc88

посмотри мой пример. там все три блока идут подряд. Никуда лазить не нужно.
Правда там обработки нет. Но её можно прямо в том же процессе и делать, отослав основному процессу сообщение: "данные получены, идёт обработка".
Одно дело, когда ты видишь try/catch/finally, другое дело, когда у тебя три функции. Первое проанализировать намного проще.
void UpdateUserList
{
try
{
userList = ServiceManager.UserService.GetAll;
}
catch (Exception e)
{
LOG.Write(e);
MessageBox.Show("Blablabla");
}
}

Я всё равно не понимаю, в чём такая ценность того, что 3 строки написаны внутри одного try. При чём друг с другом совсем не связаны в общем-то.
Ценность этого проявляется тогда, когда у тебя на поддержке несколько мегабайт кода в production. Запросов в одном экране GUI может быть десяток-другой. Если на каждый написано по три функции, пусть и в виде вложенных делегатов, ты замучаешься анализировать такой код.

Marinavo_0507

дык не плати им 120 пока не научатся :)

6yrop

Ценность этого проявляется тогда, когда у тебя на поддержке несколько мегабайт кода в production.
получается, что Java тут сильно сливает, у нее же нет yield-а?

kokoc88

получается, что Java тут сильно сливает, у нее же нет yield-а?
Не знаю, с чем связаны твои вставки про Java в каждую затычку. Я уже давно утверждал, что Java, как язык, хуже. Тем не менее, под Java выпущено несколько интересных framework-ов, которые добавляют асинхронные вызовы без необходимости менять код на асинхронный. Поэтому асинхронный код получается даже более красивым и простым, чем в C#, и именно в этой области сильно сливает скорее C#.

6yrop

Если на каждый написано по три функции, пусть и в виде вложенных делегатов, ты замучаешься анализировать такой код.
на первый взгляд (не уверен различие будет только в количестве фигурных скобочек. А анализ try охватывающего код, выполняющийся в нескольких потоках, тоже не такая уж простая задача.

kokoc88

на первый взгляд, различие будет только в количестве фигурных скобочек. А анализ try охватывающего код, выполняющийся в нескольких потоках, тоже не такая уж простая задача.
уже написал: если у тебя тонны этих фигурных (и не только) скобочек, то это начинает влиять на качество анализа и читаемость кода.

6yrop

Не знаю, с чем связаны твои вставки про Java в каждую затычку.
это чтобы поддержать твой стиль общения.
 

Тем не менее, под Java выпущено несколько интересных framework-ов, которые добавляют асинхронные вызовы без необходимости менять код на асинхронный. Поэтому асинхронный код получается даже более красивым и простым, чем в C#, и именно в этой области сильно сливает скорее C#.
 

Как на этих фреймворках выглядит код, который без yield-а требуется три блока try?

6yrop

(и не только)
на первый взгляд, только
Все же очень странно, что количество скобочек так сильно влияет на твое восприятие.

kokoc88

это чтобы поддержать твой стиль общения.
В каждой теме при неудачной попытке о чём-то поспорить, ты обязательно упоминаешь Java. Мой стиль общения тебе поддержать не удаётся: ты по-нормальному не можешь ни слиться, ни отстоять свою точку зрения.
Как на этих фреймворках выглядит код, который без yield-а требуется три блока try?
Задай вопрос по-русски.

6yrop

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

kokoc88

ты ровно про себя написал
Вот именно, я написал про себя и про то, что ты не можешь ни так, ни эдак.

6yrop

Задай вопрос по-русски.
попытайся включить голову

kokoc88

на первый взгляд, только
Ответь, что из этого является фигурными скобочками: ( ) { } =>
Все же очень странно, что количество скобочек так сильно влияет на твое восприятие.
Было бы странно, если бы количество всякой мишуры не влияло на моё восприятие. Напиши, как мой код выглядит в твоём стиле:
void UpdateUserList
{
try
{
userList = ServiceManager.UserService.GetAll;
}
catch (Exception e)
{
LOG.Write(e);
MessageBox.Show("Blablabla");
}
}

6yrop

 
: ... ты ...

Вот именно, я написал про себя и про то, что ты не можешь ни так, ни эдак.
Лол, всё же у тебя поразительное строение мозгов, заточенное только на тролинг, даже готов смысл слово “ты” поменять.

kokoc88

попытайся включить голову
Ок, давай попытаемся включить голову.
Как на этих фреймворках выглядит код, который без yield-а требуется три блока try?
Я понимаю, что ты учился в сельской школе, а на ВМК и МехМат берут фактически без проверки сочинения. Но ради уважения к себе, своему языку и людям, которые вынуждены читать твои опусы, следовало бы научиться выражать свои мысли хотя бы сопряжёнными частями речи. Для этого стоит прочитать вслух то, что ты написал. Заметь, я молчу про "тся" и "ться" - это нерешаемая кодерами задача. Но стоило бы пояснить, к чему относится глагол "требуется" в твоей фразе, и как последние четыре слова связаны с первой её частью? Любая, даже самая молодая учительница по русскому языку, подчеркнула бы эту фразу красной ручкой и поставила бы Р (речевая ошибка) на полях.
Короче, я предлагаю тебе написать асинхронный код с yield и тремя блоками try или что ты там хотел спросить.

kokoc88

Лол, всё же у тебя поразительное строение мозгов, заточенное только на тролинг, даже готов смысл слово “ты” поменять.
Поразительное строение мозгов у того, кто не мыслит логически и не пытается понять, что ему написали.
Мой стиль общения тебе поддержать не удаётся: ты по-нормальному не можешь ни слиться, ни отстоять свою точку зрения.
Поясняю смысл этой фразы: я в своём стиле общения могу либо по-нормальному слиться, либо отстоять свою точку зрения. Ты не можешь сделать ни первого, ни второго, и поэтому мой стиль общения тебе недоступен.

6yrop

Поясняю смысл этой фразы: я в своём стиле общения могу либо по-нормальному слиться, либо отстоять свою точку зрения. Ты не можешь сделать ни первого, ни второго, и поэтому мой стиль общения тебе недоступен.
читай что тебе пишут, "поддержать" это не значит перенять твой стиль общения, ни в коем случае :grin: . Попытки с тобой по хорошему приводят к тому, что ты отсылаешь без указания прямых ссылок. Поэтому приходится как-то поддерживать твои "эмоции" соответствующими вопросами.

6yrop

Короче, я предлагаю тебе написать асинхронный код с yield и тремя блоками try или что ты там хотел спросить.
здрасьте, код уже написан -ем и мы его тут обсуждаем

kokoc88

читай что тебе пишут, "поддержать" это не значит перенять твой стиль общения, ни в коем случае
Для того, чтобы что-то поддержать, надо понимать, что ты поддерживаешь. Каждый раз ты упоминаешь Java, и каждый раз получашь от неё по носу из-за отсутствия знаний по теме. Логично бы выбрать что-то другое, но ты продолжаешь то ли кусать кактус, то ли пугать ежа голой жопой.

kokoc88

здрасьте, код уже написан -ем и мы его тут обсуждаем
Пожалуйста, приведи ссылку на код, написанный , в котором есть yield и три блока try.

6yrop

Для того, чтобы что-то поддержать, надо понимать, что ты поддерживаешь. Каждый раз ты упоминаешь Java, и каждый раз получашь от неё по носу из-за отсутствия знаний по теме. Логично бы выбрать что-то другое, но ты продолжаешь то ли кусать кактус, то ли пугать ежа голой жопой.
короче, ты очередной раз перешел на личности, понятно, сливаешь

kokoc88

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

6yrop

Пожалуйста, приведи ссылку на код, написанный , в котором есть yield и три блока try.
код -я вот
это псевдокод, но (насколько я понимаю) его можно осуществить на C# либо через yield, либо через async/await (в следующей версии).
Если без yield, то потребуется три try-я: на первый GUI блок, на блок в другом потоке, на последний GUI блок.
Хотелось бы увидеть что и как там на Java в этом плане.

6yrop

на первый GUI блок
этот блок может быть пустым, но не суть

FRider

я несколько потерял нить вашего спора, но наверно поддержу шурека(!).
Оч хуево в яве без yieldа

kokoc88

это псевдокод, но (насколько я понимаю) его можно осуществить на C# либо через yield, либо через async/await (в следующей версии).
Этот псевдокод можно реализовать без yield и без async, и я привёл наглядный пример.
Если без yield, то потребуется три try-я: на первый GUI блок, на блок в другом потоке, на последний GUI блок.
Нет, если без yield, то потребуется один try. Про yield речь зашла только в рамках реализации continuations.
Хотелось бы увидеть что и как там на Java в этом плане.
Если речь идёт про эмуляцию continuations, то вместо конструкций типа yield в Java используют фреймворки, которые перезаписывают байт код. Например, из известных: RIFE и JavaFlow.

kokoc88

я несколько потерял нить вашего спора, но наверно поддержу шурека(!).
Если все друг-друга понимают, то речь про реализацию continuations в языке, где их нет; и про то, что Java якобы сливает в этом плане.
Оч хуево в яве без yieldа
Я использовал его только для эмуляции continuations , и поэтому не вижу разницы. Расскажи, для каких ситуаций его используешь ты?

FRider

для простого создания итераторов очевидно

kokoc88

для простого создания итераторов очевидно
Можно более детально рассказать, какие задачи ты этим решаешь? Пишешь свои коллекции? Приведи пример кода, чтобы я мог до конца понять, почему тебе хуёво без yield. Я бы переписал его без yield, чтобы посмотреть, что получится.

6yrop

Нет, если без yield, то потребуется один try.
без yield и без запуска другой очереди GUI сообщений? Приведи ссылку на твое сообщение, пожалуйста.

6yrop

Если речь идёт про эмуляцию continuations, то вместо конструкций типа yield в Java используют фреймворки, которые перезаписывают байт код. Например, из известных: RIFE и JavaFlow.
код привести можешь?

kokoc88

код привести можешь?
Почему ты не приводишь код на C#, а я должен приводить код на Java? Отгугли и то, и другое, объективно проанализируй, напиши пару туториалов и тогда аргументированно наезжай.

kokoc88

без yield и без запуска другой очереди GUI сообщений? Приведи ссылку на твое сообщение, пожалуйста.
Вот ещё раз код, если ты его потерял. Этот код вызывает сервис, отображая диалог с прогресс баром и кнопкой отмены на экране:
void UpdateUserList
{
try
{
userList = ServiceManager.UserService.GetAll;
}
catch (Exception e)
{
LOG.Write(e);
MessageBox.Show("Blablabla");
}
}

FRider

Ох, еще и код писать. Ну ок:

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);
}
}
}

6yrop

я бы хотел увидеть, где здесь строчки:

someButton.Enable = false; //пример UI-ного кода в начале запроса
someButton.Enable = true; //пример UI-ного кода по окончанию

kokoc88

я бы хотел увидеть, где здесь строчки:
Очевидно, что они выполняются внутри вызова GetAll

kill-still

Пользуясь случаем, поздравляю всех коллег с профессиональным праздником! :)

doublemother

Пользуясь случаем, поздравляю всех коллег с профессиональным праздником! :)
Спешишь. Завтра только.

6yrop

Очевидно, что они выполняются внутри вызова GetAll
Ок. Единственно, отмечу для себя, что UserService имеет доступ к someButton (это ни хорошо и ни плохо, просто отметил).
Если твой код имеет отношение к нашей дискуссии, то надо раскрыть некоторые детали метода GetAll: где в нем отделение работы с тредами от бизнес-логики и UI-я?

kokoc88

Ок. Единственно, отмечу для себя, что UserService имеет доступ к someButton (это ни хорошо и ни плохо, просто отметил).
С точки зрения ООП он ничего не имеет, так что я без понятия, что ты там для себя отметил. Если тебе требуется изменять доступность кнопки без модальных диалогов на экране, то можно написать это руками в моём коде: ставишь Enabled = false внутри try и = true внутри finally.
Если твой код имеет отношение к нашей дискуссии, то надо раскрыть некоторые детали метода GetAll: где в нем отделение работы с тредами от бизнес-логики и UI-я?
Метод GetAll реализован только на сервере, так что я без понятия, о чём ты.

6yrop

Метод GetAll реализован только на сервере, так что я без понятия, о чём ты.
ты же только что написал, что someButton.Enable = false внутри GetAll. Эта строчка не скомпилируется на сервере :grin: .

kokoc88

Ох, еще и код писать. Ну ок:
Понятно, это достаточно интересный пример. У меня получается несколько альтернатив без 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, и вот оно реально выглядит некрасиво.

kokoc88

ты же только что написал, что someButton.Enable = false внутри GetAll. Эта строчка не скомпилируется на сервере .
Всё скомпилируется, потому что этой строчки никогда не будет на сервере. Тебе ещё требуется информация или признаешь, что ты без понятия, как сделать такую простенькую схему неблокирующих RPC? Или, вдруг, до тебя уже дойдёт, как это сделано?

6yrop

Всё скомпилируется, потому что этой строчки никогда не будет на сервере. Тебе ещё требуется информация или признаешь, что ты без понятия, как сделать такую простенькую схему неблокирующих RPC? Или, вдруг, до тебя уже дойдёт, как это сделано?
Поскольку ты не признал противоречия в твоих двух соседних постах, имею полное право сказать, что ты слил.

kokoc88

Поскольку ты не признал противоречия в твоих двух соседних постах, имею полное право сказать, что ты слил.
Я их не признал, потому что их нет: button.Enabled = false/true может выполняться внутри вызова GetAll и это происходит на стороне клиента. При этом метод GetAll в коде реализован только на сервере. Если ты не знаешь, как это делается, то так и напиши, а не пытайся засчитывать свои собственные сливы другим.

zorin29

Я не знаю, мне расскажи!

6yrop

Я их не признал, потому что их нет: button.Enabled = false/true может выполняться внутри вызова GetAll и это происходит на стороне клиента. Если ты не знаешь, как это делается, то так и напиши, а не пытайся засчитывать свои собственные сливы другим.
ага, если только смысл слова A заменить на смысл слова B, т.е. пользоваться твоими приемами построения утверждений, то да, возможно все :grin:

kokoc88

Я не знаю, мне расскажи!
Да по сути, если почитать всю тему внимательно, последние две страницы, то можно понять и без детального рассказа. Я потом более детально опишу происходящее, если потребуется.

6yrop

Я не знаю, мне расскажи!
почти уверен, что Майк отмажется сейчас, вот смотри :)

kokoc88

ага, если только смысл слова A заменить на смысл слова B, т.е. пользоваться твоими приемами построения утверждений, то да, возможно все
У меня простой вопрос: ты знаешь, как это делается, или нет?

6yrop

я угадал :)

kokoc88

почти уверен, что Майк отмажется сейчас, вот смотри
Просто скажи, что ты не знаешь, чтобы сохранить это для истории. А потом я напишу, как можно это всё сделать.

6yrop

У меня простой вопрос: ты знаешь, как это делается, или нет?
делать что? Какую задачу решаешь ты мне стало не понятно.
Запросы к серверу с неблокирующим UI-ем, да, естественно, делаю уже много лет.

kokoc88

делать что? Какую задачу решаешь ты мне стало не понятно.
Я привёл фрагмент кода, на который ты наезжаешь и пытаешься засчитать мне какие-то сливы. При этом сливы ты почему-то пытаешься засчитать за утверждения: GetAll в коде реализуется только на стороне сервера; GUI во время вызова не блокируется, напротив, выводится диалог с marquee прогресс баром и кнопкой "Отмена", которая работает и отменяет запрос на сервер.

6yrop

Я отталкиваюсь от примера -я, поскольку это, действительно, типичный пример, если ты напишешь в своем коде someButton.Enable = true, будет понятнее.

zorin29

Да по сути, если почитать всю тему внимательно, последние две страницы, то можно понять и без детального рассказа.
Та мне детально не надо, мне бы в общих чертах понять.
У тебя есть какой-то клиентский прокси-класс, реализующий тот же интерфейс, что и серверный Service, и выполняющий асинхронный вызов с показом прогресса?
А, раз ты говоришь, что на клиенте GetAll ты не реализуешь никак, то этот прокси-класс у тебя генерируется автоматически через какой-нибудь reflection?

kokoc88

Я отталкиваюсь от примера -я, поскольку это, действительно, типичный пример, если ты напишешь в своем коде 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;
}
}

Ну что, теперь ты "вдруг" "давно понял", как всё работает, или нет?

6yrop

какой тип возвращаемого значения у GetAll?

kokoc88

А, раз ты говоришь, что на клиенте GetAll ты не реализуешь никак, то этот прокси-класс у тебя генерируется автоматически через какой-нибудь reflection?
Да, это одна из возможных реализаций данного подхода. Да и вообще, разве ты сам, руками, делаешь реализацию сервиса на клиенте?

kokoc88

какой тип возвращаемого значения у GetAll?
Поифгу, пусть будет List<UserDto>

FRider

У меня получается несколько альтернатив без yield. Не знаю, можно или нельзя из-за этого говорить про то, что жить без yield невозможно.
эээ, не. Я не говорил "невозможно" ;) Очевидно возможно, т.к. писалиж раньше без него. И даже без явы(сишарпа :grin: )
. На Java решение с итератором такое, не знаю, настолько ли страшнее твоего варианта, чтобы написать "очень хуёво"

На мой взгляд страшнее, многа буков. (Кстати на питоне этот код будет выглядеть еще более выразительно). Не спорю, что вопрос дискуссионный. Но мне лично нравится лаконичная запись с елдом. Плюс, общее решение для такого итератора врядли можно будет упаковать в один класс, скорей будет пара-тройка на несколько "общих" ситуаций
Отдельно напрягает вот этот метод:
    public boolean hasNext {
// won't handle exceptions for clarity
return hasNext = (stream.read(buffer) != 0);
}

т.к. получается эдакий сайд-эффект, поэтому второе решение красивей, но тоже громозденько.
Нащщет лямбд согласен, ага.

kokoc88

На мой взгляд страшнее, многа буков.
Даже если убрать сайд эффекты, то при использовании общего класса придётся рефакторить твой код, чтобы в нём было меньше букафф. ;) Что характерно, я с yeild вообще не сталкиваюсь, потому что проще передать в виде IEnumerable<T> какую-нибудь read only коллекцию, что на C#, что в Java. В общем, хуёво это или нет, действительно скорее тема холивара.

6yrop

Да, у меня куцые знания в многопоточности. Мне приходят на ум только извращенные варианты реализации того, как у тебя написано. Это можно сделать, но сильно извращенно. Ок, как ты это делаешь?

FRider

то характерно, я с yeild вообще не сталкиваюсь, потому что проще передать в виде IEnumerable<T> какую-нибудь read only коллекцию,
так весь смак возникает в том случае, если на момент вызова метода ты не знаешь, сколько же элементов будут засунуты в эту коллекцию.

zorin29

Да, это одна из возможных реализаций данного подхода. Да и вообще, разве ты сам, руками, делаешь реализацию сервиса на клиенте?
Да это же твоя собственная версия ChannelFactory<TInterface>, которая связана с UI! :)
Респект за обфускацию.

kokoc88

так весь смак возникает в том случае, если на момент вызова метода ты не знаешь, сколько же элементов будут засунуты в эту коллекцию.
Да, просто у меня такие задачи возникают только в серверной части при реализации continuations. Но на практике я почему-то не сталкиваюсь с необходлимостью использовать yield-ы.

kokoc88

Это можно сделать, но сильно извращенно.
Давай не наезжать на извращённость, continuations через yield тоже достаточно извращён, но писать асинхронный код без этого ты долбанёшься.

kokoc88

Да, у меня куцые знания в многопоточности. Мне приходят на ум только извращенные варианты реализации того, как у тебя написано. Это можно сделать, но сильно извращенно. Ок, как ты это делаешь?
Идея реализации примерно такая:
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.

kokoc88

Да это же твоя собственная версия ChannelFactory<TInterface>, которая связана с UI!
Метод подходит для всего: долгих вычислений, запросов через WebRequest/HttpRequest, WCF, и так далее. То есть это далеко не собственная версия ChannelFactory<TInterface>, если ты имеешь в виду WCF.

6yrop

Давай не наезжать на извращённость, continuations через yield тоже достаточно извращён, но писать асинхронный код без этого ты долбанёшься.
ну у нас вполне рабочей схемой является вот .
Мы не сталкивались с ситуацией, когда часто требовалось охватывать try-ем UI и не UI код. Т.е. проблема, которую затронул , она теоретически есть, но на практике показатели приложения/девелопмента/саппорта вряд ли значительно улучшатся при наличии такой фичи.
Ну, в конце концов, JQuery живет на таком подходе и ничего народ доволен.

6yrop

... делегируем вызов сервису в отдельном...
5. Если пользователь нажимает кнопку "Отмена", то закрываем диалог ожидания (останавливаем наш вложенный цикл обработки сообщений) и выкидываем, например, UserCanceledException.
После нажатия "Отмена" поток, в котором идет обращение к сервису, остается занятым?

kokoc88

После нажатия "Отмена" поток, в котором идет обращение к сервису, остается занятым?
А как ты думаешь?

6yrop

3. Для того, чтобы избежать огроменной кучи проблем, которые порождает event driven подход, я всегда делаю модальный диалог с кнопкой отмены. Хотя это не обязательно, и можно написать полностью event driven GUI, но я ни разу не видел, чтобы такие реализации нормально работали при наличии сложной логики в GUI.
Да, давать возможность пользователю везде кликать во время запроса, это сильное усложнение даже просто в логическом плане, а с другой стороны это нафиг не надо. Но показывать модальное окно не всегда подходит, например, кликаем по узлу дерева, надо под узлом написать "Loading..." и задизайблить большинство контролов на форме, оставив кнопу Close.

6yrop

А как ты думаешь?
Раз задаю такой вопрос, значит, да, думаю, что остается занятым пока сервис не отвалится по таймауту.

kokoc88

Но показывать модальное окно не всегда подходит, например, кликаем по узлу дерева, надо под узлом написать "Loading..." и задизайблить большинство контролов на форме, оставив кнопу Close.
Это тривиально реализуется в моей схеме, но на практике будет дико и безбожно тормозить и мигать отрисовкой контролов. Зачем делать окно модальным руками мне не понятно. Для быстрых вызовов у меня есть хитрость, функционально она заключается в том, что при быстрых вызовах диалог на экране не появляется. В общем, не пытайся высасывать из пальца какие-то недостатки.

Marinavo_0507

. Хотя это не обязательно, и можно написать полностью event driven GUI, но я ни разу не видел, чтобы такие реализации нормально работали при наличии сложной логики в GUI.
Браузеры так работают в теории :) На практике и блокируются, и глючат.

kokoc88

Раз задаю такой вопрос, значит, да, думаю, что остается занятым пока сервис не отвалится по таймауту.
По себе людей не судят.

kokoc88

Браузеры так работают в теории На практике и блокируются, и глючат.
В браузерах всё намного проще, потому что нельзя начать вторую очередь сообщений. Все самые сложные проблемы идут от этого.

6yrop

По себе людей не судят.
Тебя всё же в детстве били по голове видимо, что ж ты так к людям то относишься? Где я там людей судил?

kokoc88

Тебя всё же в детстве били по голове видимо, что ж ты так к людям то относишься?
Детка, ты опять обиделся на мой вполне нейтральный ответ? Так не суди всех разработчиков по себе, если ты забываешь или не умеешь отменять вызовы, выполняющиеся в другом потоке, то это не означает, что другие этого не умеют и/или не делают.

6yrop

Детка, ты опять обиделся на мой вполне нейтральный ответ? Так не суди всех разработчиков по себе, если ты забываешь или не умеешь отменять вызовы, выполняющиеся в другом потоке, то это не означает, что другие этого не умеют и/или не делают.
Лол, ты как будто там сидишь рефлексируешь и про себя пишешь :grin: . От того видимо путаешь "ты" и "я". Я задал вопрос, ты ответил вопросом на вопрос, на который я нормально ответил, в ответ получил от тебя наезд на меня лично. А в последнем сообщении у тебя вообще бред какой-то, ты там выходи уже из своей рефлексии.

kokoc88

Я задал вопрос, ты ответил вопросом на вопрос, на который я нормально ответил, в ответ получил от тебя наезд на меня лично.
Пупсик, ты забыл упомянуть, ради чего ты задал первый вопрос. С тем же результатом можно было бы спросить, что происходит с формой модального диалога, ведь для неё надо вызывать Dispose.
P.S. А в последнем сообщении у тебя вообще бред какой-то.

6yrop

Пупсик
может ты гей?

FRider

блин, опять тред скатился к сралову майка и шуреска :(

kokoc88

может ты гей?
Я просто пытаюсь не задеть твоих нежных детских чувств.

kokoc88

блин, опять тред скатился к сралову майка и шуреска
Срач получился потому, что Шурик понятия не имеет о том, как сделать простенькую реализацию RPC в обработчике GUI, но ему жутко хочется меня затроллить. При этом он умудряется слиться даже в той теме, где все меня троллят. :grin:
Вообще, мне пора заигнорить Шурика, потому что всё всегда происходит по примерно одинаковому сценарию. Автором выплёвывается какой-то пост, обычно в попытке восхвалить C# или написать, как же на самом деле хуёво устроена Java. Как только кто-то пытается копнуть поглубже, так оказывается, что автор совсем не в теме. Например, восхваляет "новую" технологию от Microsoft, которую реализовали на Java 5-10 лет назад.
На самом деле в теме есть куча интересной информации. Как тебе идея вынести её в отдельные посты?

6yrop

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);
}
}

Результат:

kokoc88

Вопрос: твой код работает аналогично вот этому?
Нет, лучше прочитай внимательно и задай вопросы по не понятным тебе пунктам. В моей реализации не создаётся визуального ощущения, что GUI подвисает. Потому что оно вообще не подвисает.

6yrop

Нет, лучше прочитай внимательно и задай вопросы по не понятным тебе пунктам.
Хорошо. Вот этот код выполняется в обработчике, который поступил в первый 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 это так?

kokoc88

И поток останавливается на строчке userList = ServiceManager.UserService.GetAll это так?
Ты прочитал тему, где я описываю вариант своего решения? Нет, поток не останавливается, он попадает в новую очередь сообщений.

6yrop

как ты создаешь новый message loop?

zorin29

И поток останавливается на строчке userList = ServiceManager.UserService.GetAll это так?
В Windows Forms вызов form.ShowModal запускает еще один оконный цикл, так что, если внутри GetAll открывается диалоговое окно, то подвисания GUI не происходит.

6yrop

В Windows Forms вызов form.ShowModal запускает еще один оконный цикл, так что, если внутри GetAll открывается диалоговое окно, то подвисания GUI не происходит.
тогда всё завязано на модальный диалог, а Майк говорит, что модальный диалог не принципиален.

kokoc88

как ты создаешь новый message loop?
Как душе угодно: можно начать его с помощью нового модального диалога или сделать руками.

zorin29

Ну написать

while(GetMessage
{
TranslateMessage;
DispatchMessage;
}

несложно и самостоятельно, не обязательно полагаться на диалоговые окна.

kokoc88

несложно и самостоятельно, не обязательно полагаться на диалоговые окна.
Ещё можно написать Application.DoEvents и [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern void WaitMessage но ни в том, ни в другом случае нельзя полагаться на то, что этот код постоянно выполняется и писать в цикле какую-то логику.

6yrop

сделать руками.
я не знаю как

6yrop

while(GetMessage
{
TranslateMessage;
DispatchMessage;
}
это псевдокод? или реальный?

6yrop

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

kokoc88

это псевдокод? или реальный?
То реальный код, ещё можно сделать примерно так:
    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;
}
}
}

На самом деле погугли, тема-то популярная.

6yrop

На самом деле погугли, тема-то популярная.
то что ты описал пока не нагугливается

6yrop

[DllImport("user32.dll", CharSet = CharSet.Auto)]
че та мне это кажется подозрительным, вроде задача стандартная, поэтому думается, что стоит обходиться стандартными winform-вскими средствами

kokoc88

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

6yrop

без модального диалога, то цикл сообщений, написанный вручную, будет самой незначительной твоей проблемой.
вот вот, всё как-то шатко, в том плане, что надо плясать вокруг message loop-а.
Всё же обычное решение, которое в том или ином виде представлены в JQery, GWT, .NET Web Services, WCF, RIA Services, как-то более здраво смотрятся.

kokoc88

вот вот, всё как-то шатко, в том плане, что надо плясать вокруг message loop-а.
Выдумай более реальную проблему, раз уж ты не смог понять, как добавить RPC без разламывания control flow.

6yrop

Выдумай более реальную проблему, раз уж ты не смог понять, как добавить RPC без разламывания control flow.
Да, я почти уверен, что если я предложу такое коллегам, они не оценят.

kokoc88

Да, я почти уверен, что если я предложу такое коллегам, они не оценят.
Если твои коллеги такого же уровня, как ты, то может быть и не оценят.

6yrop

Если твои коллеги такого же уровня, как ты, то может быть и не оценят.
ага, ты вообще разочарован в IT специалистах :grin:

kokoc88

ага, ты вообще разочарован в IT специалистах
Да, в тебе я действительно очень разочарован, если ты понимаешь, о чём я.

kokoc88

Кстати, почему-то никто не написал реализацию с yield. Выглядеть она могла бы вот так (если полностью следовать идее Рихтера):
        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;
}
}
Думаю, теперь достаточно дописать достоинства и недостатки yield против подхода с модальным диалогом, и выделить в отдельную тему на память.

6yrop

на память.
да, для потомков, которые будут писать исторические очерки о Великом и Непревзойдённом

kokoc88

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

6yrop

глупые у тебя вопросы

kokoc88

глупые у тебя вопросы
Ты испытваешь неловкость при упоминании бабая?

zorin29

Кстати, почему-то никто не написал реализацию с yield.
Уфф. Здорово.
Никогда раньше не думал о возможности использовать последовательность yield return как способ увязать одно за другим асинхронные действия.
По мне так это от бедности: C# не дает нормальных средств это сделать, приходится использовать не вполне подходящие.
, посоветуй книжку по таким вот "асинхронным паттернам?" Gamma&Co читал.

6yrop

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;
}
}

6yrop

По мне так это от бедности: C# не дает нормальных средств это сделать, приходится использовать не вполне подходящие.
т.к. вроде async/result в следующей версии C# под это будут заточены

6yrop

userList = ServiceManager.UserService.EndGetAll(ctx.GetAsyncResult;
Кстати, finaly мы можем писать, зато у нас появилось изменяемое состояние, мы должны следить, чтобы не вызвать EndGetAll до yield return, не приятно. В общем, без поддержки компилятора это все сыровато выглядит.

kokoc88

, посоветуй книжку по таким вот "асинхронным паттернам?" Gamma&Co читал.
Про книжку я честно не знаю, но идея изначально принадлежит Рихтеру, он описывает её в своём Concurrent Affairs и называет AsyncEnumerator. У него есть библиотека, с помощью которой можно реализовать написанный мною код, плюс-минус. Так же его идея реализована другими людьми, но с использованием IEnumerable<Action> или других перечислений.

6yrop

Кстати, приведу вариант с моим подходом (на самом деле это аля JQuery и .т.п. подход):

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;
});

Dasar

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

Dasar

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

Dasar

и будем его вызывать:
ты хочешь чтобы messagebox вызвался последовательно после ВыполнитьЗапрос, или параллельно?
если параллельно, то значит либо ты, либо движок - должен задать правила разрешения конфликтов.
в данном случае, один из способов разрешения конфликта, указать, что MessageBox требует, чтобы кнопка была enable-нутая, и если это не так, то возникает ошибка (возможен вариант: ожидается когда она такой станет)

Dasar

Я вообще не понимаю, в чём фишка делать "мегафункцию", которая и отправляет и обрабатывает. Это ж GUI. всё на событиях всё равно.
кнопку нажал - отослался запрос
если вместо одной моей строчки тебе пришлось написать 20-строк, то сколько у тебя будет строк, если необходимо описать какое-нибудь реальное поведение?..

Phoenix

не понял тогда пример. try мгновенно что ли выполняется?
Я думал, что то, что в try выполняется много-много времени. и общем случае это может быть синхронный вызов, который нельзя остановить, который подвис(пусть даже на таймаут в 10 минут). Как мне объяснили, такие библиотеки лучше не использовать и их почти нет.

Phoenix

если вместо одной моей строчки тебе пришлось написать 20-строк, то сколько у тебя будет строк, если необходимо описать какое-нибудь реальное поведение?..
21 :) в том-то и дело, что я самую общую часть почти описал. И твое решение выглядит просто вот на таком простом примере. а ассимптотически(при увеличении сложности) моё попроще.

6yrop

ты хочешь чтобы messagebox вызвался последовательно после ВыполнитьЗапрос, или параллельно?
если параллельно, то значит либо ты, либо движок - должен задать правила разрешения конфликтов.
в данном случае, один из способов разрешения конфликта, указать, что MessageBox требует, чтобы кнопка была enable-нутая, и если это не так, то возникает ошибка (возможен вариант: ожидается когда она такой станет)
Вот вот, я об этом и говорю, еще надо специфицировать "правила разрешения конфликтов". И программистам требуется держать это в голове. Это как сейчас Extract Method при наличии yield-а отличается от обычного. Например,
 

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 писать? А так обычные колбеки (с известным поведением во многих сценариях) рулят :), и не надо огород городить.

kokoc88

Кстати, приведу вариант с моим подходом (на самом деле это аля 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.

Phoenix

А можно код для интересующихся приводить?
 
Вот шурик код, который я смог скомпилить и получил тот же самый эффект.
А то начали с обсуждения мультитред-мультипроцесс, а сейчас уже какие-то RPC пошли, GetAll и так далее.
RPС всё-таки это c# больше вещь, я в ней не очень разбираюсь.

kokoc88

А можно код для интересующихся приводить?
Мы обсуждаем вещи, для которых 10-20 строк кода будет недостаточно. А ты можешь положить добрую традицию и написать свой многопроцессный код?
А то начали с обсуждения мультитред-мультипроцесс, а сейчас уже какие-то RPC пошли, GetAll и так далее.
В данном ответвлении обсуждается удобный способ писать неблокирующие GUI RPC.
RPС всё-таки это c# больше вещь, я в ней не очень разбираюсь.

RPC это вовсе не C#

Dasar

не понял тогда пример. try мгновенно что ли выполняется?
это CPS
описывается как синхронный, но реально такой код при исполнении переписывается в async или event-driven

Dasar

Вот вот, я об этом и говорю, еще надо специфицировать "правила разрешения конфликтов".
их в любом случае, необходимо специфицировать.
если используется ED, и несколько окон, то все равно придется как-то где-то записать, что выполнение части сообщений должно отмениться при закрытия окна.
> Это не поломанное control flow?
ты хочешь поговорим о том, как писать код, у которого есть side-эффекты?
это не ко мне. имхо, такого кода просто не должно быть. и в целом, это реально добиться

6yrop

ED
что это?

Phoenix

RPC- remote procedure call? где кроме винды она используется?
Просто начали с темы, которая мне была интересна, а сейчас какие-то 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

pilot

RPC- remote procedure call? где кроме винды она используется?
import xmlrpclib  

Phoenix

Понял. отсылается не запрос с параметрами, а вызывается функция, где есть вместо параметра имя функции и параметры. а на серваке регистрируются обработчики.
Тогда беру слова про RPC обратно. Ничего ужасного.

Dasar

И твое решение выглядит просто вот на таком простом примере. а ассимптотически(при увеличении сложности) моё попроще.
давай возьмем сложный пример. например, копирование файлов

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;
}
}
}

как это будет выглядеть у тебя?

Dasar

ED
что это?
event-driven

6yrop

Кроме того, в твоём коде не показано, как сменить сервис с UserService на, скажем, DocumentService.
надо всего лишь передать IUserService как generic параметр.
Можно так:

RemoteCaller<IUserService>.Call(service => ...);

можно так:

RemoteCaller.Create<IUserService>.Call(service => ...);

6yrop

такого кода просто не должно быть
не понял, у тебя нигде нет side эффектов? а, например, с базой как работаешь?

6yrop

зачем using, если у на side эффектов нет :grin: ?

Phoenix

Издеваешься?
Написал синхронный код со словами: "А давай перепиши его в ассинхронный, который будет проще". А у меня мой синхронный код, будет исполняться параллельно благодаря суперкомпилятору.
Я ж не спорю, что руками будет сложнее.
Давай реализацию Transaction включим в твой пример и прочих ассинхронных вариантов и сравним.
вопрос-то о чём был: что сложнее, процессами или тредами. Я у тебя тредов не вижу. Это ты считаешь, что там треды, а вдруг там разработчики тесты провели и оказалось, что процессами круче и у них там в дочернем процессе исполяется.
 
И твое решение выглядит просто вот на таком простом примере. а ассимптотически(при увеличении сложности) моё попроще.

Это относилось к тому, что если всё синхронно писать и будем много разных событий и по каждому будет запускаться отдельный тред и что-то синхронно делать, то централизованно хранить состояния и обрабатывать события проще.
Ты же сейчас говоришь, что всё это где-то будет исполняться прозрачно от реализации вообще. Без тредов, без процессов и ассинхронности.
А если пишешь синхронно и понятно, а исполняется асинхронно - то это супер. Я вообще не знал, что так можно.

Dasar

не понял, у тебя нигде нет side эффектов? а, например, с базой как работаешь?
изменение данных - не side-эффект, а явное изменение состояния, удовлетворяющее куче соглашений

Dasar

> Давай реализацию Transaction включим в твой пример и прочих ассинхронных вариантов и сравним.
код драйвера transacted NTFS тоже включить?
Это ты считаешь, что там треды, а вдруг там разработчики тесты провели и оказалось, что процессами круче и у них там в дочернем процессе исполяется.
я так не считаю.
данный код можно запустить и через очередь сообщений, и через треды(или тредпул можно и через процессы, в зависимости от написанного провайдера, которому этот код передается на исполнение.
> вопрос-то о чём был: что сложнее, процессами или тредами.
в данной подветке, скорее от вопроса "как лучше исполнять - тредами или процессами?" перешли к вопросу "как лучше описывать?"
ps
по поводу тредов и процессов, вроде же определились в плюсах и минусах:
треды будут быстрее, если есть большое состояние в памяти которое надо менять
процессы, по умолчанию, лучше масштабируется (но и на тредах, если руководствоваться несколькими правилами, это также можно обеспечить; на процессах таких правил меньше)

6yrop

изменение данных - не side-эффект, а явное изменение состояния, удовлетворяющее куче соглашений
если принять такое твое извращенное понятия side эффекта, тогда не понятно, что криминального ты увидел в моем коде?

6yrop

там всего лишь "явное изменение состояния"

Dasar

там всего лишь "явное изменение состояния"
ты смешал два разных подхода - у тебя yield-ы используются: и для описания асинхронного кода, и для описания итератора данных - и у тебя ожидаемо получилась хрень.
если так не делать, то и хрени не будет. на это даже можно создать правило fxcop

Phoenix

в данной подветке, скорее от вопроса "как лучше исполнять - тредами или процессами?" перешли к вопросу "как лучше описывать?"

Да вроде очевидно, что чем проще написано - тем круче.
Я просто считал (да и продолжаю наверно что в теории все эти красивости очень вкусные, а когда дело доходит до реализации, то какая-нибудь жопа вылезает.
Потому как мне плохо понятно, как можно в одном ED обработчике можно предусмотреть всё, что будут использовать. Ведь здесь идея-то такая. Пишите, как вам проще и самую суть, а мы исполним, как будет лучше и быстрее.
Всё равно придётся свой new Transaction писать или старый расширять. А там-то и придётся реализовывать всё.
Хотя опять же, тему знаю... чуть поболе, чем совсем не знаю. Поэтому может мои взгляды и устарели.

6yrop

code:
пользователь нажал кнопку
  using(показывать-что-команда-выполняется
  {
     экран.определенная-область = cервер.выполнить-запрос;
  }
Вот пробная реализация using сценария :):
 

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;

Dasar

Вот пробная реализация using сценария :):
имхо, единственный плюс, что такая запись держится начиная с c# 3.0
Оставить комментарий
Имя или ник:
Комментарий: