Изучаю .NET, возникают вопросы .
В частности, не совсем понятно как решается проблема отмены асинхронных вызовов.
К примеру, у меня есть слушающий сокет, которому я сказал BeginAccept.
В какой-то момент, я решаю остановить прослушивание и вызываю socket.Close().
Что произойдет, если как раз в этот момент исполняется код из accept callback-а?
Ведь он может обращаться к объектам которые уже disposed, верно?
Следущий код, демонстрирует проблему (на примере таймера):
using System;
using System.ComponentModel;
using System.Timers;
namespace test
{
class Test
{
private Timer timer;
private System.Threading.AutoResetEvent evt;
private void OnTimer(object sender, ElapsedEventArgs args)
{
evt.Set(); // <-- Упс! Object disposed.
}
public void RunTest()
{
using (evt = new System.Threading.AutoResetEvent(false))
{
using (timer = new Timer())
{
timer.Elapsed += this.OnTimer;
timer.Interval = 10;
timer.Enabled = true;
evt.WaitOne(9, false);
timer.Enabled = false;
}
}
}
public static void Run()
{
var test = new Test();
test.RunTest();
}
}
class Program
{
private static void Main(string[] args)
{
while (true)
{
Test.Run();
}
}
}
}
Собственно вопрос состоит в следующем: есть ли стандартное средство, позволяющее дождаться завершения исполняющихся асинхронных вызовов перед dispos-ом объекта? Либо нужно городить дополнительную синхронизацию самому (очень не хочется).
Здравствуйте, jedi, Вы писали:
J>Собственно вопрос состоит в следующем: есть ли стандартное средство, позволяющее дождаться завершения исполняющихся асинхронных вызовов перед dispos-ом объекта? Либо нужно городить дополнительную синхронизацию самому (очень не хочется).
J>Спасибо!
Есть такое средство
IAsyncResult ar = socket.BeginAccept();
ar.AsyncWaitHandle.WaitOne();
Здравствуйте, Аlexey, Вы писали:
А>Есть такое средство А>
А>IAsyncResult ar = socket.BeginAccept();
А>ar.AsyncWaitHandle.WaitOne();
А>
Чудесное средство, только не для того случая, который я описал.
Еще раз — я хочу остановить сервер и отменить прослушку. Насколько я понял,
официальный способ — это Close. Далее, проблемы описаны в первом посте.
Здравствуйте, jedi, Вы писали:
J>В частности, не совсем понятно как решается проблема отмены асинхронных вызовов. J>К примеру, у меня есть слушающий сокет, которому я сказал BeginAccept. J>В какой-то момент, я решаю остановить прослушивание и вызываю socket.Close(). J>Что произойдет, если как раз в этот момент исполняется код из accept callback-а? J>Ведь он может обращаться к объектам которые уже disposed, верно?
Если "остановка прослушивания" — штатная операция (а иначе и быть не может, ибо к остановке приводит явный вызов), то нет ничего страшного в том, что бы хранить специальный флаг, сообщающий о том, "остановлена ли прослушка".
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, jedi, Вы писали:
J>>В частности, не совсем понятно как решается проблема отмены асинхронных вызовов. J>>К примеру, у меня есть слушающий сокет, которому я сказал BeginAccept. J>>В какой-то момент, я решаю остановить прослушивание и вызываю socket.Close(). J>>Что произойдет, если как раз в этот момент исполняется код из accept callback-а? J>>Ведь он может обращаться к объектам которые уже disposed, верно?
_FR>Если "остановка прослушивания" — штатная операция (а иначе и быть не может, ибо к остановке приводит явный вызов), то нет ничего страшного в том, что бы хранить специальный флаг, сообщающий о том, "остановлена ли прослушка".
В общем, Вы правы. Вот только флагом (булевской переменной) здесь не обойтись. По крайней мере, я не вижу как это сделать?
Если не сложно, не могли бы Вы показать как это сделать в примере с таймером?
И еще = я знаю про Timer.Dispose(WaitHandle) но, к сожалению в том же сокете аналогов нет,
поэтому нужно более общее решение.
Здравствуйте, jedi, Вы писали:
J>К примеру, у меня есть слушающий сокет, которому я сказал BeginAccept. J>В какой-то момент, я решаю остановить прослушивание и вызываю socket.Close(). J>Что произойдет, если как раз в этот момент исполняется код из accept callback-а? J>Ведь он может обращаться к объектам которые уже disposed, верно?
Disposed в этом случае может быть только один объект — оригинальный слушающий сокет. Ну получите исключение ObjectDisposedException при вызове EndAccept(). Пока проблем не вижу. Я что-то не понимаю ?
J>Следущий код, демонстрирует проблему (на примере таймера):
Э-э-э... Так Вам сокеты, или всё-таки что-то другое ?
Здравствуйте, drol, Вы писали:
D>Здравствуйте, jedi, Вы писали:
D>Disposed в этом случае может быть только один объект — оригинальный слушающий сокет.
Не совсем. Может быть что угодно, зависит от логики программы.
В частности, в примере, происходит обращение к убитому ивенту.
D>Ну получите исключение ObjectDisposedException при вызове EndAccept(). Пока проблем не вижу. Я что-то не понимаю ?
Я как-то не привык, чтоб у меня такие исключения просто так летали. Может тяжелое наследие С++
D>Э-э-э... Так Вам сокеты, или всё-таки что-то другое ?
Пример с таймером — для простоты. На самом деле проблема общая, не только сокетов касается ...
Здравствуйте, jedi, Вы писали:
D>>Disposed в этом случае может быть только один объект — оригинальный слушающий сокет. J>Не совсем. Может быть что угодно, зависит от логики программы. J>В частности, в примере, происходит обращение к убитому ивенту.
Ну так не надо делать такую логику, иначе зачем Вам асинхронное I/O сотоварищи ?
D>>Ну получите исключение ObjectDisposedException при вызове EndAccept(). Пока проблем не вижу. Я что-то не понимаю ? J>Я как-то не привык, чтоб у меня такие исключения просто так летали.
EndAccept() может бросать объекты минимум 6-ти классов исключений. Это не касаясь того, что код делегата работает в потоке из пула. В связи с чем в его теле не помешает ловить вообще всё что летит. Как минимум с целью логирования...
J>Может тяжелое наследие С++
Привыкайте
J>Пример с таймером — для простоты.
Как раз пример с таймером сложнее, на мой взгляд. Бо там может быть непонятно сколько одновременно исполняющихся вызовов делегата, и непонятно сколько ещё в очереди (и есть ли она вообще ???)
Тогда как для нормальных реализаций на BeginXXX()/EndXXX() срабатываний делегата в итоге будет столько, сколько прошло успешных вызовов BeginXXX()
J>На самом деле проблема общая, не только сокетов касается ...
В случае сокетов вызов Socket.Close() иницирует срабатывание всех "ждущих" делегатов успешных вызовов BeginXXX(). Так что достаточно, например, завести семафор/событие и по нему дожидаться окончания отработки делегатов.
Состояние закрытия, как уже выше предлагали, можно отслеживать хоть булевым флагом (конечно, с volatile). Ну а методы сокета в соответствующих случаях бросают исключения.
*Да, и не забывайте, делегаты Timer'а и BeginXXX() работают в потоках из пула, а они все background-потоки...
Здравствуйте, Аlexey, Вы писали:
J>>Собственно вопрос состоит в следующем: есть ли стандартное средство, позволяющее дождаться завершения исполняющихся асинхронных вызовов перед dispos-ом объекта? Либо нужно городить дополнительную синхронизацию самому (очень не хочется).
А>Есть такое средство А>
А>IAsyncResult ar = socket.BeginAccept();
А>ar.AsyncWaitHandle.WaitOne();
А>
Это средство не поможет. В случае сокета, WaitOne() ожидает (если не вдаваться в подробности) не окончание исполнения делегата, а момент отправки делегата на исполнение.
Здравствуйте, drol, Вы писали:
D>Здравствуйте, Аlexey, Вы писали:
D>Это средство не поможет. В случае сокета, WaitOne() ожидает (если не вдаваться в подробности) не окончание исполнения делегата, а момент отправки делегата на исполнение.
Можно ссылку на документацию где это сказано? (хочется разобраться в деталях)
Здравствуйте, drol, Вы писали:
D>*Да, и не забывайте, делегаты Timer'а и BeginXXX() работают в потоках из пула, а они все background-потоки...
Как все сложно... Кстати, не могли бы Вы ткнуть в какую-нибудь опенсорсную реализацию сервера на .NET, которая по-Вашему может служить хорошим примером для изучения этих вещей.
Здравствуйте, jedi, Вы писали:
D>>Это средство не поможет. В случае сокета, WaitOne() ожидает (если не вдаваться в подробности) не окончание исполнения делегата, а момент отправки делегата на исполнение. J>Можно ссылку на документацию где это сказано?
Читайте доку на IAsyncResult и его окрестности.
J>(хочется разобраться в деталях)
Да там вообще-то всё просто. AsyncWaitHandle возвращает объект для ожидания окончания асинхронного вызова. Асинхронный вызов в нашем случае это не вызов делегата. Это вызов какого-нибудь AcceptEx() для Win32'шного сокета где-то в дебрях BeginAccept(). И взведение AsyncWaitHandle означает лишь то, что теперь можно спокойно дёргать EndAccept(), и получать результат асинхронного вызова без блокировки. В нашем случае — новый сокет с установленным соединением.
Можно, например, вообще вот так писать:
var r = sock.BeginAccept(null, null);
var c = sock.EndAccept(r);
И всё будет работать, просто EndAccept() заблокируется до момента установления соединения.
Чтобы было удобней устраивать сложные внешние синхронизации (ну там через WaitForMultipleObjects() какие-нибудь), объект ожидания вытащили прямиком в IAsyncResult.AsyncWaitHandle:
var r = sock.BeginAccept(null, null);
r.AsyncWaitHandle.WaitOne();
var c = sock.EndAccept(r);
Работает аналогично первому примеру.
Ещё один способ ожидания: поллинг через IAsyncResult.IsCompleted Иногда требуется
Ну и, наконец, то что мы обсуждали с самого начала: делегат, вызывающийся в некотором потоке из пула, после окончания отработки исходного (настоящего) асинхронного вызова.
*Надеюсь стало яснее
Здравствуйте, jedi, Вы писали:
J>Как все сложно... Кстати, не могли бы Вы ткнуть в какую-нибудь опенсорсную реализацию сервера на .NET, которая по-Вашему может служить хорошим примером для изучения этих вещей.
Я всё как-то в области close source работал последнее время С ходу даже и не подскажу на что можно посмотреть так, чтобы понятно было.
*Кстати, совсем недавно я пытался починить одну проприетарную .NET/C# либу как раз такого толка — асинхронное сетевое I/O. У нас кончилась лицензия на апгрейды (давным-давно кончилась ), а тут клиент переехал на толстую многопроцессорную + многоядерную машинку, и вдруг полезли нездоровые глюки.
Так вот, я опух разбираться в её внутренностях. И это при наличии полных source'ов! Так за разумное время ничего толком и не раскопал Ну кроме того, что основная часть проблем была связана всё-таки с конфигурацией ОС
Здравствуйте, jedi, Вы писали:
J>Может тяжелое наследие С++
Если Вы раньше писали на С++, то обязательно прочтите это. Так, например, вы узнаете, что Thread.Abort()(.NET)и TerminateThread() (Win32) это не одно и тоже, поэтому можно не пичкать код событиями синхронизации,(Manual/AutoResetEvent) для корректного завершения потоков, как это было в С++(Event). А также разницу между lock() парой EnterCriticalSection(), LeaveCriticalSection ().
J>В частности, не совсем понятно как решается проблема отмены асинхронных вызовов.
Асинхронные операции такие, как BeginInvokе, BeginAccept и все остальные BeginXxx исполняются в потоке пула потоков и являются background потоками, так же как и их колбеки. После того, как такой поток был запущен, остановить его нельзя(можно, но это плохая идея, т.к по окончанию задачи такие потоки должны вернуться обратно в пул).
«Мы с тобой в чудеса не верим, Оттого их у нас не бывает…»
Здравствуйте, jedi, Вы писали:
_FR>>Если "остановка прослушивания" — штатная операция (а иначе и быть не может, ибо к остановке приводит явный вызов), то нет ничего страшного в том, что бы хранить специальный флаг, сообщающий о том, "остановлена ли прослушка".
J>…Вот только флагом (булевской переменной) здесь не обойтись. По крайней мере, я не вижу как это сделать?
private Timer timer;
private System.Threading.AutoResetEvent evt;
private bool timerAborted = false;private void OnTimer(object sender, ElapsedEventArgs args)
{
if(!timerAborted) {// Если timerAborted, то обрабатывать ничего и не надо
evt.Set(); // "Нормальная" обработка события}//if
}
public void RunTest()
{
using (evt = new System.Threading.AutoResetEvent(false))
using (timer = new Timer())
{
timerAborted = false;
timer.Elapsed += this.OnTimer;
timer.Interval = 10;
timer.Enabled = true;
evt.WaitOne(9, false);
timer.Enabled = false;
timerAborted = true;// на самом деле, гарантировать,
// что обработчик события не вызовется между
// "timer.Enabled = false;" тут нельзя, но оно и не нужно.
}
}
хотя, в данном конкретном случае с таймером, можно попросту вместо флага timerAborted проверять значение свойства timer.Enabled.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>хотя, в данном конкретном случае с таймером, можно попросту вместо флага timerAborted проверять значение свойства timer.Enabled.
Вы действительно считаете что это будет работать? Хинт: добавьте логирование исключения в консоль и попробуйте позапускать.
Здравствуйте, jedi, Вы писали:
_FR>>хотя, в данном конкретном случае с таймером, можно попросту вместо флага timerAborted проверять значение свойства timer.Enabled.
J>Вы действительно считаете что это будет работать? Хинт: добавьте логирование исключения в консоль и попробуйте позапускать.
Добавил. Только вместо System.Timers.Timer я взял System.Threading.Timer:
namespace test
{
using System;
using System.Diagnostics;
using System.Threading;
class Test
{
private Timer timer;
private AutoResetEvent evt;
//private bool timerAborted = false;public void RunTest() {
TimerCallback callback = state => {
try {
//if(!timerAborted) {
evt.Set();
//}//if
Debug.Print("Success");
} catch(ObjectDisposedException ex) {
Debug.Print("Exception: {0}", ex);
}//try
};
using(evt = new AutoResetEvent(false))
using(timer = new Timer(callback, null, 0, 10)) {
evt.WaitOne(9, false);
//timerAborted = true;
}
}
public static void Run() {
var test = new Test();
test.RunTest();
}
}
class Program
{
private static void Main(string[] args) {
while(true) {
Test.Run();
}
}
}
}
Так вот после раскоментирования timerAborted тест не валится Да и с чего бы?
Вообще, пример мог бы быть и таким:
using System;
using System.Diagnostics;
using System.Threading;
internal static class Program
{
private static void Main() {
while(true) {
bool timerAborted = false;
TimerCallback callback = state => {
try {
if(!timerAborted) {
((EventWaitHandle)state).Set();
}//if
} catch(ObjectDisposedException ex) {
Debug.Print("Exception: {0}", ex);
}//try
};
using(var evt = new AutoResetEvent(false))
using(var timer = new Timer(callback, evt, 0, 10)) {
evt.WaitOne(9, false);
//timerAborted = true;
}//using
}//while
}
}
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, drol, Вы писали:
D>Чтобы было удобней устраивать сложные внешние синхронизации (ну там через WaitForMultipleObjects() какие-нибудь), объект ожидания вытащили прямиком в IAsyncResult.AsyncWaitHandle: D>
D>var r = sock.BeginAccept(null, null);
D>r.AsyncWaitHandle.WaitOne();
D>var c = sock.EndAccept(r);
D>
D>Работает аналогично первому примеру.
Не знаю, есть ли это в .Net, но снятие асинхронного запроса в Win32 есть — CancelIO.
Здравствуйте, _FRED_, Вы писали:
_FR> using(var timer = new Timer(callback, evt, 0, 10)) {
Во-первых, Вы перепутали параметры конструктора таймера.
[msdn]
public Timer(TimerCallback callback, Object state, int dueTime, int period)
dueTime
The amount of time to delay before callback is invoked, in milliseconds. Specify Timeout..::.Infinite to prevent the timer from starting. Specify zero (0) to start the timer immediately.
period
The time interval between invocations of callback, in milliseconds. Specify Timeout..::.Infinite to disable periodic signaling.
[/msdn]
Таким образом, таймер у Вас файрится сразу же, еще до его диспоза. Поэтому, никакого исключения не происходит.
Во-вторых вы используете Debug.Print, т.е. судя по всему запускаете пример под дебаггером. Это в корне меняет поведение многопоточной программы.
В-третьих (это мой недочет) нужно поставить 10 миллисекунд в вейте: evt.WaitOne(10, false). Это намного повышает вероятность появления исключения. Еще лучше поставить и таймеру и вейту 1 мс, чтобы увеличить кол-во прогонов в единицу времени. Это тоже увеличт вероятность воспроизведения бага.
В результате имеем такой код:
namespace test
{
using System;
using System.Diagnostics;
using System.Threading;
class Test
{
private Timer timer;
private AutoResetEvent evt;
private bool timerAborted = false;
public void RunTest()
{
TimerCallback callback = state =>
{
try
{
if (!timerAborted)
{
// Context switch #1
evt.Set();
}
}
catch (ObjectDisposedException ex)
{
Console.WriteLine("Exception: {0}", ex);
}
};
using (evt = new AutoResetEvent(false))
using (timer = new Timer(callback, null, 1, 0))
{
evt.WaitOne(1, false);
// Context switch 2
timerAborted = true;
}
}
public static void Run()
{
var test = new Test();
test.RunTest();
}
}
class Program
{
private static void Main(string[] args)
{
while (true)
{
Test.Run();
}
}
}
}
Если его скомпилировать и запустить на исполнение (не под дебаггером), то на моей двухпроцессорной машине исключение происходит довольно регулярно (иногда — сразу после старта программы, иногда через пару минут).
Также вероятнось воспроизведения повышается, если запустить несколько копий программы.
Это типичный детский баг с потоками. Причем очень трудно воспроизводимый и приводящий к крешу "раз в месяц в
висимости от фазы луны". Конечно, приложения могут жить (и живут) с подобными багами десятилетиями. Но,
пардон, я не хочу быть писателем таких приложений.
Причина бага очень простая. Представьте что выполнение потока из пула (исполняющего код колбека таймера) прерывается в точке "Context switch #1" — после проверки флага. В этот момент исполнение главного потока
продолжается в точке "Context switch #2" — тут мы устанавливаем флаг и счастливо грожаем таймер и ивент.
Теперь, когда исполнение таймерного колбека возобновится, он обратится к убитому ивенту.
Сразу после отправки сообщения, допер как сделать баг 100% воспроизводимым.
Достаточно эмулировать переключение контекста сразу после проверки флага в колбеке. Вот так:
if (!timerAborted)
{
Thread.Sleep(0); // имитируем переключение контекста
evt.Set();
}
Здравствуйте, _FRED_, Вы писали:
_FR>>>Если "остановка прослушивания" — штатная операция (а иначе и быть не может, ибо к остановке приводит явный вызов), то нет ничего страшного в том, что бы хранить специальный флаг, сообщающий о том, "остановлена ли прослушка". J>>…Вот только флагом (булевской переменной) здесь не обойтись. По крайней мере, я не вижу как это сделать?
private bool timerAborted = false;
Во-первых Вы забыли volatile
Во-вторых, какой-нибудь вызов OnTimer() уже может начать исполняться к моменту выхода из using'а, пройти проверку флага, но ещё не дойти до evt.Set() И тогда опять-таки всё может сдохнуть...
Ещё раз повторяю, тут нужен нормальный семафор/событие. Плюс посмотреть как работает этот Timer по части наличия/логики очереди отложенных вызовов OnTimer()
Здравствуйте, jedi, Вы писали:
J>Сразу после отправки сообщения, допер как сделать баг 100% воспроизводимым. J>Достаточно эмулировать переключение контекста сразу после проверки флага в колбеке. Вот так:
Точно, я ошибся И всё из-за того, что сам не редко критикую: лень.
Надо было добавить критическую секцию так вот:
Здравствуйте, _FRED_, Вы писали:
_FR>Точно, я ошибся И всё из-за того, что сам не редко критикую: лень. _FR>Надо было добавить критическую секцию так вот:
А ни убивает ли это саму идею асинхронного исполнения на корню?
Здравствуйте, drol, Вы писали:
D>Ещё раз повторяю, тут нужен нормальный семафор/событие. Плюс посмотреть как работает этот Timer по части наличия/логики очереди отложенных вызовов OnTimer()
Меня вот что удивляет. Вопрос-то элементарный, но никто не может показать работающего кода
Неужели все мифы правда и программеры на .NET просто не думают о таких вещах?
Здравствуйте, jedi, Вы писали:
_FR>>Точно, я ошибся И всё из-за того, что сам не редко критикую: лень. _FR>>Надо было добавить критическую секцию так вот:
J>А ни убивает ли это саму идею асинхронного исполнения на корню?
Почему? Wait-то не в локе. Лочится лишь установка флага и сброс эвента.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Почему? Wait-то не в локе. Лочится лишь установка флага и сброс эвента.
Ну, на каждый тик таймера мы должны лочиться, что само по-себе неприятно. Конечно, в нормальной ситуации,
никто с нами за этот лок не конкурирует, поэтому оверхед небольшой. Но все же он есть ...
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Не знаю, есть ли это в .Net, но снятие асинхронного запроса в Win32 есть — CancelIO.
Мы покамест только достаточно простые ситуации разбираем Закрытие того же сокета, например, автоматически cancel'ит все I/O запросы к нему, включая асинхронные.
Здравствуйте, jedi, Вы писали:
_FR>>Почему? Wait-то не в локе. Лочится лишь установка флага и сброс эвента.
J>Ну, на каждый тик таймера мы должны лочиться, что само по-себе неприятно. Конечно, в нормальной ситуации, J>никто с нами за этот лок не конкурирует, поэтому оверхед небольшой. Но все же он есть ... J>
Ну уж если критическая секция "убивает … саму идею асинхронного исполнения на корню"… Помойму как раз для этого они и были сделаны. Нет?
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, drol, Вы писали:
D>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Не знаю, есть ли это в .Net, но снятие асинхронного запроса в Win32 есть — CancelIO.
D>Мы покамест только достаточно простые ситуации разбираем Закрытие того же сокета, например, автоматически cancel'ит все I/O запросы к нему, включая асинхронные.
И ждет завершения всех запущенных колбеков? Если нет, то все проблемы остаются. А MSDN, как всегда, все эти тонкости скромно умалчивает
Здравствуйте, _FRED_, Вы писали:
_FR>Ну уж если критическая секция "убивает … саму идею асинхронного исполнения на корню"… Помойму как раз для этого они и были сделаны. Нет?
В идеальном мире, идеальная многопоточная программа вообще не должна юзать никаких локов.
Всегда надо стремиться к идеалу
Здравствуйте, drol, Вы писали:
_FR>>Надо было добавить критическую секцию так вот: D>Уже лучше. Осталось решить последнюю проблему: потоки из пула это background-потоки
И в чём их особенность в данном случае…?
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, jedi, Вы писали:
_FR>>Ну уж если критическая секция "убивает … саму идею асинхронного исполнения на корню"… Помойму как раз для этого они и были сделаны. Нет?
J>В идеальном мире, идеальная многопоточная программа вообще не должна юзать никаких локов. J>Всегда надо стремиться к идеалу
В идеальной программе вообще ничего не надо знать о существовании многопоточности
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, jedi, Вы писали:
D>>Мы покамест только достаточно простые ситуации разбираем Закрытие того же сокета, например, автоматически cancel'ит все I/O запросы к нему, включая асинхронные. J>И ждет завершения всех запущенных колбеков? Если нет, то все проблемы остаются.
Дык а почему она должна их ждать ? Это ведь асинхронное I/O, в том и суть, что никто ничего не блокирует.
J>А MSDN, как всегда, все эти тонкости скромно умалчивает
MSDN большой Вы просто не там смотрите. В описании closesocket(), например, всё написано достаточно чётко:
An application should not assume that any outstanding I/O operations on a socket will all be guaranteed to completed when closesocket returns. The closesocket function will initiate cancellation on the outstanding I/O operations, but that does not mean that an application will receive I/O completion for these I/O operations by the time the closesocket function returns. Thus, an application should not cleanup any resources (WSAOVERLAPPED structures, for example) referenced by the outstanding I/O requests until the I/O requests are indeed completed.
Здравствуйте, drol, Вы писали:
D>MSDN большой Вы просто не там смотрите.
Приступая к изучению .NET, я оджидал, что он облегчит мне жизнь, а не усложнит или оставит все как есть ...
К тому же Вы сейчас привязались к конкретной реализации (MS.NET). Обстоят дела также в случае того же Mono?
Здравствуйте, _FRED_, Вы писали:
D>>Уже лучше. Осталось решить последнюю проблему: потоки из пула это background-потоки _FR>И в чём их особенность в данном случае…?
В том же что и всегда: их пристреливают на месте, как только все не-background завершаются. А уважаемый jedi хочет гарантированной полной отработки всех запущенных callback'ов.
Здравствуйте, drol, Вы писали:
D>>>Уже лучше. Осталось решить последнюю проблему: потоки из пула это background-потоки _FR>>И в чём их особенность в данном случае…? D>В том же что и всегда: их пристреливают на месте, как только все не-background завершаются. А уважаемый jedi хочет гарантированной полной отработки всех запущенных callback'ов.
Мне казалось, наоборот, надо отсечь (и не обрабатывать) колбеки, вызванные после наступления некоего события под условным названием "отмена запроса".
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Мне казалось, наоборот, надо отсечь (и не обрабатывать) колбеки, вызванные после наступления некоего события под условным названием "отмена запроса".
Скорее так: я хочу чтоб Close() подождал окнчания уже заупущенных колбеков и гарантировал, что не будет новых.
Здравствуйте, jedi, Вы писали:
_FR>>Мне казалось, наоборот, надо отсечь (и не обрабатывать) колбеки, вызванные после наступления некоего события под условным названием "отмена запроса".
J>Скорее так: я хочу чтоб Close() подождал окнчания уже заупущенных колбеков и гарантировал, что не будет новых.
Здравствуйте, jedi, Вы писали:
D>>Уже лучше. Осталось решить последнюю проблему: потоки из пула это background-потоки J>А Вы бы показали нам грешным что да как, да научили бы ...
Дык я уже ведь говорил, что в случае Timer'а надо его внутреннее устройство знать. А смотреть лень
Вот про сокеты код есть, но он проприетарный, большой, и непонятный. Проще заново написать, а я в отпуске... Может попробую завтра накидать примерчик
Здравствуйте, _FRED_, Вы писали:
_FR>А как подождать завершения подсказали в первом же ответе
Так вот подсказали неправильно. AsyncWaitHandle.WaitOne() ожидает завершение/отмену асинхронного вызова, AcceptEx() в данном случае, а не callback'а запускаемого после него.
Здравствуйте, drol, Вы писали:
_FR>>А как подождать завершения подсказали в первом же ответе
D>Так вот подсказали неправильно. AsyncWaitHandle.WaitOne() ожидает завершение/отмену асинхронного вызова, AcceptEx() в данном случае, а не callback'а запускаемого после него.
Ну ОК, тогда EndInvoke можно позвать. Или, если EndInvoke по каким-либо соображениям не устраивает, завести свой объект синхронизации, который выставлять при завершении. Сути это не меняет, главное, что принципиально осуществимо.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, jedi, Вы писали:
J>Всем привет.
J>Изучаю .NET, возникают вопросы . J>В частности, не совсем понятно как решается проблема отмены асинхронных вызовов. J>К примеру, у меня есть слушающий сокет, которому я сказал BeginAccept. J>В какой-то момент, я решаю остановить прослушивание и вызываю socket.Close(). J>Что произойдет, если как раз в этот момент исполняется код из accept callback-а? J>Ведь он может обращаться к объектам которые уже disposed, верно?
J>Следущий код, демонстрирует проблему (на примере таймера):
Запустил я у себя этот пример, никакой проблемы не обнаружил. Что я не привильно сделал ? Или может быть он не достаточно долго проработал ? В течении 10 мин полет нормальный.
Здравствуйте, jedi, Вы писали:
J>Приступая к изучению .NET, я оджидал, что он облегчит мне жизнь, а не усложнит или оставит все как есть ...
На всей планете программистов знающих что такое асинхронное I/O от силы доли процента. А уж из тех кто на .NET/Java работает так и вообще. High Performance I/O весьма экзотическая область, мэйнстрим вовсе не там.
*А Вы вот возьмите, да напишите сами красивую либу
J>К тому же Вы сейчас привязались к конкретной реализации (MS.NET). Обстоят дела также в случае того же Mono?
Mono это ваши личные проблемы Стандарты связанные с .NET соответствующие библиотеки никак не покрывают, насколько я в курсе. И Microsoft насчёт не своих платформ ничего не обещала...
Здравствуйте, drol, Вы писали:
D>На всей планете программистов знающих что такое асинхронное I/O от силы доли процента. А уж из тех кто на .NET/Java работает так и вообще. High Performance I/O весьма экзотическая область, мэйнстрим вовсе не там.
Я не думаю, что все так печально. Ничего сакрального в асинхронном вводе-выводе нет.
D>*А Вы вот возьмите, да напишите сами красивую либу
Да я как бы губу раскатал, что уже написано до меня .
Я наверное еще поищу, почитаю что умные люди пишут. Может кто из местных корифеев подтянется к обсуждению
и просветит меня.
Вообще, мне .нет по работе сейчас на фиг не нужен (я пишу на плюсах). Это скорее развлечение —
потыкать в него, посмотреть что к чему, может понравится и проникнусь. А может и плюну через время