.NET асинхронные методы в NewotkStream
зачем методу EndRead блокировать выполнение?а что он, по-твоему, должен делать?
В полностью асинхронном сценарии вызывается метод Begin* а обработка результатов происходит при вызове callback-а.
End* нужен, например, в синхронно выполняемой функции, если возвращаемое значение определяется результатом асинхронной операции - вызвали BeginSmth, потом что-то посчитали, а перед тем местом, где нужен этот результат, вызываем EndSmth
а что он, по-твоему, должен делать?Выполнять неблокирующее чтение из буфера.
В полностью асинхронном сценарии вызывается метод Begin* а обработка результатов происходит при вызове callback-а.
В полностью асинхронном сценарии обратный вызов происходит тогда, когда может быть выполнена неблокирующая операция.
End* нужен, например, в синхронно выполняемой функции
End* нужен, например, везде.
upd.
End* нужен, например, везде.
да, я некорректно написал. Имел в виду вызов End* до завершения операции, думал, это и так понятно.
но .NET - говно
Мы, видимо, о каких-то разных .NET фреймворках говорим. я не встречал ни в одном классе BCL асинхронной операции, у которой метод End* был бы неблокирующийНет, это не мы говорим о разных .NET Framework, это ты говоришь только про .NET Framework. С точки зрения других технологий, которых существует ещё дофига, такое поведение операций Begin/End нелогично.
Меня интересует только один вопрос, работают ли методы BeginWrite/EndWrite как полноценные асинхроные вызовы, потому что в документации может быть ошибка. (В глубине BeginWrite я нашёл ожидание события на Thread Pool, но весь код детально не анализировал.)
да, я некорректно написал. Имел в виду вызов End* до завершения операции, думал, это и так понятно.
Судя по документации, EndWrite всегда вызывается до завершения операции. (Если пул потоков не задыхается.)
В глубине BeginWrite я нашёл ожидание события на Thread PoolЯ посмотрел и ничего подобного не нашёл — оно вызывает Socket.BeginReceive, то вызывает Socket.DoBeginReceive, а то вызывает нативный WSARecv с оверлаппед структурой. Так что всё должно быть ок.
Правда, я смотрел в 2.0 фреймворке (первый попавшийся открытый в рефлекторе, лол так что может что-то поменяли. Энивей, даже если там используется какой-то тред-пул, оно же вроде умеет мультиплексировать множество блокирующегося IO поверх небольшого количества тредов, в чём весь и понт?
EDIT: Не смог удержаться и посмотрел на всякий случай в 4.0 — то же самое.
Судя по документации, EndWrite всегда вызывается до завершения операции.
посмотрел документацию по NetworkStream; однако, там майкрософтовцы отошли (интересно, почему?) от ими же описанного паттерна, в котором
an AsyncCallback delegate that references a method that is called when the asynchronous operation completes
работают ли методы BeginWrite/EndWrite как полноценные асинхроные вызовы
т.е. отправляются ли данные до того, как будет вызван EndWrite? весьма странно, если бы это было не так. Хотя мне непонятно, на кой черт в таком случае требовать наличие callback-а и зачем он вызывается сразу же (при выходе из NetworkStream.BeginWrite).
Документация написана исключительно коряво.
Была бы она написана нормально, я бы не задавал такого вопроса, да?
Я посмотрел и ничего подобного не нашёл — оно вызывает Socket.BeginReceive, то вызывает Socket.DoBeginReceive, а то вызывает нативный WSARecv с оверлаппед структурой. Так что всё должно быть ок.Посмотри поглубже (код в .NET ещё хуже, чем в JDK). WSASend с OVERLAPPED никак не противоречит тому, что callback сразу же кладётся в пул, где он при наличии свободных потоков сразу же вызывается и ждёт завершения операции в EndWrite.
Фактически, картина рисуется либо такая, не противоречащая документации:
BeginWrite -> overlapped WSASend -> post callback to thread pool
callback -> EndWrite -> wait for overlapped event and all bytes -> release resources
Либо такая, противоречащая документации:
BeginWrite -> overlapped WSASend -> register wait for event on thread pool
thread pool callback -> send more bytes -> reregister callback
all bytes sent -> .NET callback -> EndWrite -> release resources
Какой смысл говорить о других технологиях, когда в .NET Framework свои правила?Смысл в том, что я тебе пытался объяснить свой вопрос. Который ты до сих пор не понял.
т.е. отправляются ли данные до того, как будет вызван EndWrite? весьма странно, если бы это было не так.Нет. Мне пофиг, когда отправляются данные. Для высоконагруженного решения важно, чтобы пул потоков не задыхался в попытке переслать данные по медленному каналу. Поэтому нужно, чтобы обратный вызов выполнялся по факту завершения передачи всех байтов (или ошибки). Тот факт, что данные отправляются до вызова EndWrite (в чём я убедился по коду .NET почти сразу никак не помогает решить эту проблему.
Мне пофиг, когда отправляются данные. Для высоконагруженного решения важно, чтобы пул потоков не задыхался в попытке переслать данные по медленному каналу.Насколько я понял, вызывается просто делегат по завершении операции. А внутрях там что-то типа селекта стоит. Когда кто-то готов к пересылке. он пересылает. Почему он задыхаться-то должен?
Возможно, я ошибаюсь. я юзаю SocketAsyncEventArgs, там точно так. В доках было, что begin** отличаются от SocketAsyncEventArgs только тем, что заново создаётся много всяких объектов, а SocketAsyncEventArgs можно исползьовать повторно.
** это про socket.beginsend.
Насколько я понял, вызывается просто делегат по завершении операции. А внутрях там что-то типа селекта стоит. Когда кто-то готов к пересылке. он пересылает. Почему он задыхаться-то должен?Внимательно прочитай тему. В частности, цитату про BeginWrite/EndWrite из MSDN.
Я говорю о том, что идеа ассинхронности реализована с помощью BeginSend. В конце операции вызывается AsyncCallback.
В networkstream BeginWrite и EndWrite выполняют совсем другие функции.
В частности EndWrite - это не колбэк, а просто функция для ожидания треда-посыльщика (по аналогии с tread.start tread.join
Эти методы вообще из System.IO.Stream.
В networkstream BeginWrite и EndWrite выполняют совсем другие функции.Прочитай первый пост в этой теме ещё раз. Это я выяснил ещё при прочтении документации. Вопрос был простой, что криво сделано: документация или реализация.
В частности EndWrite - это не колбэк, а просто функция для ожидания треда-посыльщика (по аналогии с tread.start tread.join
EndWrite - это не просто функция ожидания. Почитай MSDN.
Частично нашёл. То есть нашёл вот это: BaseOverlappedAsyncResult.OverlappedCallback. В упор не вижу там никакого EndWrite, если ты видишь его где-то ещё, скажи, где.
Алсо, почитай же документацию на ThreadPool.UnsafeRegisterWaitForSingleObject, а потом — на нативную RegisterWaitForSingleObject, после этого все вопросы должны отпасть в принципе.
То есть картина такая:
BeginWrite -> overlapped WSASend -> Register own callback in a ThreadPool ->
IO completed -> Own callback called, some resources released, user callback called ->
User callback calls EndWrite to release the rest of the resources.
Допустима ли соответствующая интерпретация документации — вопрос открытый но не очень существенный. Кстати, фразу "Your callback method should implement the EndWrite method" видимо следует понимать заменив implement на call.
Also,
EndWrite - это не просто функция ожидания. Почитай MSDN.— что именно почитать? Потому что как я это вижу, один-в-один аналог Thread.Join (и далее в глубину времён, wait/waitpid): блокируется, если операция ещё не завершилась, следует вызвать в любом случае чтобы освободить ресурсы.
Алсо, почитай же документацию на ThreadPool.UnsafeRegisterWaitForSingleObject, а потом — на нативную RegisterWaitForSingleObject, после этого все вопросы должны отпасть в принципе.Эту документацию я читал лет пять назад, и вчера я проверил, что за это время в этой документации ничего не изменилось. Вопросы должны отпасть, когда станет ясно, как на самом деле всё это работает. Для этого надо либо внимательно прочитать код DoBeginSend либо найти того, кто это сделал и понимает что там происходит.
Например, если EndSend вызывается по факту отправки всех данных или ошибки, то надо понимать, что где-то должен быть цикл вызовов на пуле потоков: send -> проверка общего количества отправленных данных -> send more или callback. Ты его нашёл или как?
то надо понимать, что где-то должен быть цикл вызовов на пуле потоковИли, как вариант, можно понимать, что EVENT в WSAOVERLAPPED работает не так, как обратный вызов, и ставится только по факту передачи всех данных. Не могу найти этого в документации, и вообще новая 2010 документация малость отстойная (с дубликатами, признанный Microsoft-ом баг).
Например, если EndSend вызывается по факту отправки всех данных или ошибки, то надо понимать, что где-то должен быть цикл вызовов на пуле потоков: send -> проверка общего количества отправленных данных -> send more или callback. Ты его нашёл или как?Нет, я его не нашёл и не очень понимаю, с чего ты вообще взял, что он существует. В WSASend передаётся объём всех данных сразу и она вызовет наш коллбэк только когда они все отправились. Наш коллбэк — это BaseOverlappedAsyncResult.OverlappedCallback. Там что-то освобождается и вызывается пользовательский коллбэк. Который должен вызвать EndSend, который совершенно не заблокируется, потому что все данные уже посланы.
Если у тебя есть какие-то сомнения потому что ты склонен искать скрытый смысл в словах полуграмотного техникал райтера, который пишет "implements" вместо "calls" и бодро говорит, что это "the system" (то есть как бы сам фреймворк) вызывает EndWrite, хотя совершенно очевидно, что идёт речь о пользовательском коллбэке в любом случае, ну возьми да и проверь — пошли куда-нибудь мегабайт хуйни и напиши Debug.Assert(asyncResult.Completed) перед EndWrite в коллбэке.
Или можешь внимательно прочитать все двадцать строк кода DoBeginSend — надеюсь, ты его не в IL читаешь же?
Или, как вариант, можно понимать, что EVENT в WSAOVERLAPPED работает не так, как обратный вызов, и ставится только по факту передачи всех данныхWAT.
Оно работает точно так же, как обратный вызов, и ставится только по факту передачи всех данных. Покажи мне хоть полслова в WSASend которые могут заронить подозрение в обратном. Напротив, там даже так говорится:
If this function is completed in an overlapped manner, it is the Winsock service provider's responsibility to capture the WSABUF structures before returning from this call. This enables applications to build stack-based WSABUF arrays pointed to by the lpBuffers parameter.
То есть все данные, которые ты ей передал, должны быть скопированы во внутренние буфера. Что ты после этого в коллбэке-то будешь делать и зачем?
Покажи мне хоть полслова в WSASend которые могут заронить подозрение в обратном.Прочитай всю документацию, и подозрения зародятся. Но я уже во всём разобрался, просто отгуглив что там к чему: асинхронная частичная передача данных возможна только для неблокирующих сокетов, не использующих OVERLAPPED.
Так что остаётся детально разобраться в коде .NET, чтобы понять, когда всё-таки выполняется обратный вызов. Дело в том, что кроме OVERLAPPED там есть механизм на IOCP. Надо понять, какой именно используется.
Если всё это понять, тогда можно будет дать однозначный ответ, врёт ли документация.
асинхронная частичная передача данных возможна только для неблокирующих сокетов, не использующих OVERLAPPED.
Слово "асинхронная" тут совершенно лишнее, если ты имеешь в виду тупо таймаут = 0.
Алсо, lpCompletionRoutine is ignored for nonoverlapped sockets.
Слово "асинхронная" тут совершенно лишнее, если ты имеешь в виду тупо таймаут = 0.Нет, я имею в виду неблокирующий не-OVERLAPPED сокет. В этом случае совершенно точно возможна ситуация частичной передачи данных.
Алсо, lpCompletionRoutine is ignored for nonoverlapped sockets.И чё? Сокет можно ещё селектить, поллить и IOCP-ить. (Хотя последнее тоже потребует OVERLAPPED.)
Оставить комментарий
kokoc88
Столкнулся с интересными замечаниями в описании NetworkStream. Какая-то мутная тема, и кажется, расходится с тем, что написано в исходниках .NET Framework.То есть, если у меня тысячи клиентов с медленным соединением, то рабочий пул потоков будет задыхаться в ожидании отправки данных? Выходит, эти методы вовсе не работают как асинхронный ввод-вывод?
Здесь тоже не всё ясно. Если обратный вызов происходит при наличии данных для чтения, то зачем методу EndRead блокировать выполнение?