Здравствуйте, jazzer, Вы писали:
J>Ну так тогда придется где-то хранить эти хэндлы, так что тот же гемор, вид сбоку
это не вид сбоку, это доп. рычаг, который позволяет в другой манере работать с теми же вещами. и может гораздо проще решить проблему fdn721
это все равно, что отождествить все языки программирования "они же все эквивалентны машине Тьюринга". хотя разные языки одну и ту же задачу могут решать по-разному в плане перформанса, простоты кода\сопровождения и тд.
J>Далее, зачем обязательно убивать сокет явно? Достаточно хранить его в любом умном указателе и передавать явно в континуацию. Тогда при закрытии сокета в континуацию придет operation_aborted — ты в результате просто не поставишь в очередь очередную континуацию (которая держала бы сокет на плаву), и сокет мирно помрет сам вместе с указателем.
работа с внешними ресурсами часто требует полного и явного контроля. если говорить о сокетах, то иногда нужно явно освободить порт. полагаться на shared_ptr я бы не стал из-за непредсказуемости кол-ва владельцев (более точно из-за непредсказуемости момента уничтожения захваченного объекта)
J>ЗЫ Есть какой-нть нормальный перевод для continuation? Калька — это уродство какое-то "Продолжение" тоже плохо выглядит, так как имеет самостоятельный смысл. "Функция/процедура продолжения"?
обычно переводят, как "продолжение".
у слова "функция" и "процедура" тоже много значений в русском языке, главное — привыкнуть
но в данном контексте все же это слово не следует употреблять, у него несколько другое значение : http://en.wikipedia.org/wiki/Continuation
в данном случае мы все же работаем с async method handler, то есть с обработчиками асинхронных операций, которые в свою очередь часто являются замыканиями ( http://en.wikipedia.org/wiki/Closure_(computer_programming) ) имхо
U>а пример вот о чем: U>1) создается объект U>2) запускается асинхр операция по коннекту (или чтению данных) U>3) уничтожается объект явным образом
При использовании stackless coroutines так же как и в обычном случае — при входе в автомат делается проверка error_code. И если там ошибка — то передача продолжения в следующую асинхронную операцию не происходит, текущее продолжение тихо завершается и либо самоудаляется, либо уменьшает ref count.
В случае stackful — при ошибке происходит либо выброс исключения (стэк раскручивается и корутина завершается), либо же можно получить тот же error_code и самому решить что делать дальше.
Оба примера есть в Asio (1, 2), там разве что не хватает вызова close.
Здравствуйте, uzhas, Вы писали:
J>>Ну так тогда придется где-то хранить эти хэндлы, так что тот же гемор, вид сбоку U>это не вид сбоку, это доп. рычаг, который позволяет в другой манере работать с теми же вещами. и может гораздо проще решить проблему fdn721 U>это все равно, что отождествить все языки программирования "они же все эквивалентны машине Тьюринга". хотя разные языки одну и ту же задачу могут решать по-разному в плане перформанса, простоты кода\сопровождения и тд.
Покажи как бы выглядел код использующий Asio с этими хэндлерами.
J>>Далее, зачем обязательно убивать сокет явно? Достаточно хранить его в любом умном указателе и передавать явно в континуацию. Тогда при закрытии сокета в континуацию придет operation_aborted — ты в результате просто не поставишь в очередь очередную континуацию (которая держала бы сокет на плаву), и сокет мирно помрет сам вместе с указателем. U>работа с внешними ресурсами часто требует полного и явного контроля.
Часто нужен не явный контроль, а всего лишь prompt finalization, который прекрасно обеспечивается RAII.
U>если говорить о сокетах, то иногда нужно явно освободить порт. полагаться на shared_ptr я бы не стал из-за непредсказуемости кол-ва владельцев
Не надо раздавать shared_ptr кому попало.
U>(более точно из-за непредсказуемости момента уничтожения захваченного объекта)
Если в какой-то точке программы (в другом потоке?) нужно дождаться момента разрушения какого-то объекта, то необходимо делать блокирующее ожидание.
U>обычно переводят, как "продолжение". U>у слова "функция" и "процедура" тоже много значений в русском языке, главное — привыкнуть U>но в данном контексте все же это слово не следует употреблять, у него несколько другое значение : http://en.wikipedia.org/wiki/Continuation
Ты ссылаешься на то continuation, которое подразумевается в call-with-current-continuation. Есть же второе значение, которое в подразумевается в continuation-passing style.
Тем не менее эти значения очень близки. Я бы даже сказал что это не разные значения continuation, а само понятие continuation более общее, которое и покрывает эти оба конкретных случая — CPS или call/cc.
U>в данном случае мы все же работаем с async method handler, то есть с обработчиками асинхронных операций, которые в свою очередь часто являются замыканиями ( http://en.wikipedia.org/wiki/Closure_(computer_programming) ) имхо
Продолжения тоже часто являются замыканиями.
Я считаю что вполне уместно называть хэндлер асихнронной операции продолжением — это уже устоявшаяся практика.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Покажи как бы выглядел код использующий Asio с этими хэндлерами.
например, так:
class MyClient
{
protected:
tcp::socket m_socket;
task<void> m_ConnectTask;
public:
bool Open()
{
....
m_ConnectTask = m_socket.async_connect( m_endpoint, boost::bind( &MyClient::HandleConnect, this, _1 ) );
}
void Close()
{
m_socket.close();
m_ConnectTask.wait(); //blocks if there is incomplete async op, non blocking if there is no associated async op
}
protected:
void HandleConnect( const boost::system::error_code& err )
{
//do something
}
}
int main()
{
...
MyClient *client = new MyClient();
client->Open();
client->Close();
delete client;
...
}
EP>Часто нужен не явный контроль, а всего лишь prompt finalization, который прекрасно обеспечивается RAII.
я не вижу, как RAII поможет в примере выше. более того, socket.close — это не RAII, однако без него было бы еще сложнее =)
EP>Не надо раздавать shared_ptr кому попало.
дисциплину тяжело крыть автотестами, нужны более понятные средства, помогающие писать надежный софт
EP>Если в какой-то точке программы (в другом потоке?) нужно дождаться момента разрушения какого-то объекта, то необходимо делать блокирующее ожидание.
EP>Ты ссылаешься на то continuation, которое подразумевается в call-with-current-continuation. Есть же второе значение, которое в подразумевается в continuation-passing style.
в CPS тоже речь не о колбеке, а о целой ветке исполнения
EP>Продолжения тоже часто являются замыканиями.
согласен
EP>Я считаю что вполне уместно называть хэндлер асихнронной операции продолжением — это уже устоявшаяся практика.
а я считаю наоборот, это плохая практика, также как и писать "длинна", хоть это и распространенная практика
Здравствуйте, uzhas, Вы писали:
J>>Далее, зачем обязательно убивать сокет явно? Достаточно хранить его в любом умном указателе и передавать явно в континуацию. Тогда при закрытии сокета в континуацию придет operation_aborted — ты в результате просто не поставишь в очередь очередную континуацию (которая держала бы сокет на плаву), и сокет мирно помрет сам вместе с указателем.
U>работа с внешними ресурсами часто требует полного и явного контроля. если говорить о сокетах, то иногда нужно явно освободить порт.
socket.close() его _уже_ освободил, не?
U>полагаться на shared_ptr я бы не стал из-за непредсказуемости кол-ва владельцев (более точно из-за непредсказуемости момента уничтожения захваченного объекта)
порт уже будет освобожден при помощи socket.close(). shared_ptr просто удалит неактивный объект, когда asio дернет все продолжения, которые с ним связаны (c operation_aborted).
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, uzhas, Вы писали:
U>>полагаться на shared_ptr я бы не стал из-за непредсказуемости кол-ва владельцев (более точно из-за непредсказуемости момента уничтожения захваченного объекта)
J>порт уже будет освобожден при помощи socket.close(). shared_ptr просто удалит неактивный объект, когда asio дернет все продолжения, которые с ним связаны (c operation_aborted).
Всё маленько сложнее, хэндлеры(продолжения) могут в момент socket.close() исполняться. Если после socket.close() сразу разрывать связь MyClient с остальной программой (освободить m_dataProcessor), то мы нарвёмся на очень нехорошую ситуацию.
Можно использовать week_ptr<IDataProcessor>, но это спасёт только от нулевого указателя. Мы всё равно можем получить вызов m_dataProcessor->ProcessData(...) когда считаем что соединение уже завершено. Это неприятный побочный эффект, который надо учитывать в IDataProcessor.
Можно в каждый хэндлер и Close добавить приметив синхронизации, и проверять состояние сокета. Но это костыль.
Можно руками следить за текущим состоянием всех хэндлеров и ждать в Close их завершения, и это тоже костыль.
PS В общем после попытки использовать asio осталось стойкое впечатление что я что-то делаю не так. Но ни где не написано как надо делать. Всё больше советов от теоретиков, типа используй shared_from_this и всё получится.
Здравствуйте, fdn721, Вы писали:
F>Здравствуйте, jazzer, Вы писали:
J>>Здравствуйте, uzhas, Вы писали:
U>>>полагаться на shared_ptr я бы не стал из-за непредсказуемости кол-ва владельцев (более точно из-за непредсказуемости момента уничтожения захваченного объекта)
J>>порт уже будет освобожден при помощи socket.close(). shared_ptr просто удалит неактивный объект, когда asio дернет все продолжения, которые с ним связаны (c operation_aborted).
F>Всё маленько сложнее, хэндлеры(продолжения) могут в момент socket.close() исполняться.
asio тут ни при чем вообще.
У тебя просто некорректная многопоточная работа с разделяемым объектом, не защищенным мьютексом.
Эта проблема у тебя будет абсолютно везде, хоть с asio, хоть с рукопашными pthreads.
Нельзя допускать, что в одном потоке ты пишешь в сокет (или куда угодно еще, хоть в memory mapped segment), а в другом — его (сокет/сегмент) убиваешь, и это может происходить одновременно.
В принципе нельзя. Надо обязательно защищать, никаких тут других вариантов нет, это же классические гонки по граблям.
F>Если после socket.close() сразу разрывать связь MyClient с остальной программой (освободить m_dataProcessor), то мы нарвёмся на очень нехорошую ситуацию.
А зачем ее сразу разрывать, если это просто указатель? Просто дождаться нормальной смерти MyClient никак нельзя?
F>Мы всё равно можем получить вызов m_dataProcessor->ProcessData(...) когда считаем что соединение уже завершено.
Это называется data race. Asio тут ни при чем.
F>PS В общем после попытки использовать asio осталось стойкое впечатление что я что-то делаю не так. Но ни где не написано как надо делать. Всё больше советов от теоретиков, типа используй shared_from_this и всё получится.
Я, как практик asio, имею в своем MyClient конечный автомат (обычный enum). И когда мни приходит в голову прибить клиента и его многочисленные сокеты/таймеры (например, по таймеру "что-то давно не было хартбита с той стороны") — я перевожу его состояние в состояние "сворачиваемся". После чего продолжения отработают и перезапущены не будут, и объект мирно умрет.
ЗЫ readed — такого слова нет. Read — неправильный глагол, у него все три формы совпадают.
F>Можно использовать week_ptr<IDataProcessor>, но это спасёт только от нулевого указателя. Мы всё равно можем получить вызов m_dataProcessor->ProcessData(...) когда считаем что соединение уже завершено. Это неприятный побочный эффект, который надо учитывать в IDataProcessor.
F>Можно в каждый хэндлер и Close добавить приметив синхронизации, и проверять состояние сокета. Но это костыль.
F>Можно руками следить за текущим состоянием всех хэндлеров и ждать в Close их завершения, и это тоже костыль.
F>PS В общем после попытки использовать asio осталось стойкое впечатление что я что-то делаю не так. Но ни где не написано как надо делать. Всё больше советов от теоретиков, типа используй shared_from_this и всё получится.
Как уже написал jazzer, обычно в активных объектах такого типа можно использовать некий конечный автомат, так что в состоянии `closed connection` обработка данных не будет выполняться.
Но, в принципе, волшебства не будет — в многопоточной программе данные придется защищать. Как вариант, можно поставить на исполнение функцию закрытия сокета в очередь к другим обработчикам с помощью `io_service::post([](){ m_socket.close(); })`
Еще желательно помнить, что если асинхронные обработчики выполняются в пуле потоков, то для соблюдения последовательности вызовов обработчиков нужно использовать io_service::strand.
Подход с coroutines, упомянутый Evgeny.Panasyuk выглядит проще, но это тоже нужно разбираться и читать документацию. Я на практике еще не использовал coroutines.
Здравствуйте, Vain, Вы писали:
PM>>int main() PM>>{ PM>> ... PM>> boost::shared_ptr<MyClient> client = boost::make_shared<MyClient>(); PM>> client->Open(); PM>> client->Close(); //<<---- Тут ASIO запланирует вызов HandleConnect со значением error::operation_aborted. PM>>// delete client; //<<---- не нужно, объект будет удален, когда на него не останется ссылок PM>> ... PM>>} V>Так тут точно такая же проблема: client будет удалён также после выхода из блока, что не гарантирует что все хендлеры завершаться до него.
Вообще то этот пример код не является минимально полным рабочим, ни у fdn721, ни у меня. Обычно таки в программе есть что-то типа io_thread.join() которое гарантирует завершение обработчиков asio до выхода из какого-то блока.
V>Вообще сам подсчёт ссылок не решает проблем с удалением данных после завершения выполнения кода связанного с этими данными. А иногда делает даже хуже, т.к. ты не знаешь в какой момент объект разрушиться, нет вообще гарантий, что при написании какого-то кусочка кода объект в этот момент не будет жить или наоборот будет разрушен. Каждый раз при добавлении подобного кода приходится проходить мысленно по всей модели взаимодействий кода и данных. А это задалбывает.
Серебряной пули нет Я вообще не часто использую shared_ptr, но для откладывания на потом решения, когда удалить объект он полезен. Мысленно ходить далеко не надо — объект не разрушится, пока на него есть ссылки. Для слабых ссылок есть weak_ptr, для которого проверяется результат lock()
Проверяли же 15 лет назад сырые указатели на NULL и не жаловались. А некоторые до сих пор агитируют за ручное управление памятью.
Здравствуйте, uzhas, Вы писали:
EP>>Покажи как бы выглядел код использующий Asio с этими хэндлерами. U>например, так: U>
U> m_socket.close();
U> m_ConnectTask.wait(); //blocks if there is incomplete async op, non blocking if there is no associated async op
U>
А каков механизм блокирования? Там могут быть разные случаи:
1. Всё происходит в одном потоке, в котором запущен io_service.
Заблокировать поток нельзя, так как тогда не выполнится хэндлер — получаем deadlock. Нужно внутри wait'а крутить io_service, например через run_one. Но, если соединений очень много, и каждое будет делать такой wait, можем запросто получить stackoverflow — конечно можем увеличить адресное пространство стэка, но как-то получается не айс.
2. В одном потоке запущен io_service, а ждать нужно в другом, в котором он не запущен. Здесь достаточно future.
3. Есть два потока, в каждом из которых крутится свой io_service, либо один и тот же. Полностью блокировать плохо, нужно крутить run_one — что опять же может привести к stackoverflow.
EP>>Часто нужен не явный контроль, а всего лишь prompt finalization, который прекрасно обеспечивается RAII. U>я не вижу, как RAII поможет в примере выше. более того, socket.close — это не RAII, однако без него было бы еще сложнее =)
socket.close это действительно не RAII. RAII помогает освободить ресурсы в этих хэндлерах после этого явного close, причём как prompt finalization.
EP>>Не надо раздавать shared_ptr кому попало. U>дисциплину тяжело крыть автотестами,
Можно ограничивать доступ через private.
U>нужны более понятные средства, помогающие писать надежный софт
Проблема с ресурсами в контексте асинхронных обработчиков есть и в GC языках — там это один из основных use-case'ов для утечек, только помимо этого ещё и отсутствует нормальное prompt finalization.
EP>>Если в какой-то точке программы (в другом потоке?) нужно дождаться момента разрушения какого-то объекта, то необходимо делать блокирующее ожидание. EP>>Ты ссылаешься на то continuation, которое подразумевается в call-with-current-continuation. Есть же второе значение, которое в подразумевается в continuation-passing style. U>в CPS тоже речь не о колбеке, а о целой ветке исполнения
Так при использовании Asio тоже приходится делать целую ветку исполнения.
EP>>Я считаю что вполне уместно называть хэндлер асихнронной операции продолжением — это уже устоявшаяся практика. U>а я считаю наоборот, это плохая практика, также как и писать "длинна", хоть это и распространенная практика
У тебя может есть ссылка на значимый первоисточник, в котором чётко написано что такое handler, а что такое continuation?
Если такого каноничного источника нет, то и приходится опираться на распространенную практику
Здравствуйте, niXman, Вы писали:
F>> MyClient *client = new MyClient();
Можно подумать, от auto client = std::make_shared<MyClient>() что-то изменится в понимании примера.
Здравствуйте, flаt, Вы писали:
F>Можно подумать, от auto client = std::make_shared<MyClient>() что-то изменится в понимании примера.
ну, значит автору того хэлоуворда есть еще куда учиться бессмысленности...
надеюсь, в следующий раз, в хэлоуворд, он обязательно впишет ваш, бессмысленный для хэлоуворда, пример
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>Здравствуйте, flаt, Вы писали:
F>>Можно подумать, от auto client = std::make_shared<MyClient>() что-то изменится в понимании примера. X>ну, значит автору того хэлоуворда есть еще куда учиться бессмысленности... X>надеюсь, в следующий раз, в хэлоуворд, он обязательно впишет ваш, бессмысленный для хэлоуворда, пример
У тебя уже два абсолютно бессмысленных поста. Не надоело сотрясать воздух? Ест что написать по темы? Нет? Давай, до свиданья!
ага. все, кто с тобой не согласны, или говорят то, что тебе не удобно — ведьмы и еретики, и их всех нужно сжечь. можешь сколько угодно выдавать желаемое за действительное, и сколь угодно лгать себе, но повторюсь: если ты ТАКОЕ пишешь в хэлоуворде — твои проблемы не в asio, от слова совсем.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>ага. все, кто с тобой не согласны, или говорят то, что тебе не удобно — ведьмы и еретики, и их всех нужно сжечь. можешь сколько угодно выдавать желаемое за действительное, и сколь угодно лгать себе, но повторюсь: если ты ТАКОЕ пишешь в хэлоуворде — твои проблемы не в asio, от слова совсем.
1) Что такое хэлоуворд знаешь? Что-то я сомневаюсь. Это не хэлоуворд. Это пример, который я на коленке написал за 30 секунд.
2) У меня всего один вопрос, как закрыть сокет и дождаться освобождения всех ресурсов самым простым способом?
Что-то ни кто на него не так и не ответил. Ты похоже тоже не можешь на него ответить.
3) С кем я не согласился? Я всего лишь указал ряд проблем в том или ином подходе.
И наконец прилетаешь ты, и начинаешь мне рассказывать про опасность простых указателей, и про то что я ни чего не понимаю. Гениально!