Здравствуйте.
1)
Сейчас имеется приблизительно такой код обработки потока сокета в стиле APM:
| APM |
| public void ReadAsync()
{
try
{
if (_disposed)
{
return;
}
var syncObj = new SyncObjTcp {Stream = Stream, Received = new byte[4096]};
Stream.BeginRead(syncObj.Received, 0, 4096, OnBeginRead, syncObj);
}
catch(Exception ex)
{
Log.Write(ex);
}
}
private void OnBeginRead(IAsyncResult result)
{
if (_disposed)
{
return;
}
try
{
var dataLen = Stream.EndRead(result);
if (dataLen > 0)
{
var data = ((SyncObjTcp)result.AsyncState).Received.Take(dataLen).ToArray();
//1й вариант
HandleData(data);
ReadAsync();
//2й вариант
// ReadAsync();
// HandleData(data);
return;
}
if (dataLen == 0)
{
CloseFromRead();
}
}
catch(Exception ex)
{
//catch logic
}
}
|
| |
Метод HandleData может бы давольно емким, соотв. мы не будем вычитывать из сокета до тех пор пока не обработаем текущую порцию данных (1-вариант в коде);
2-й вариант (закоментированный фрагмент) асинхронный, т.е. как только мы вычитали данные, мы сразу же читаем новую порцию.
В связи с этим вопрос: если использовать 2-й вариант, возможет ли rc между обработкой данных, т.к. скорее всего это будут разные потоки, соотв. разные ядра и может получиться
так, что тот, кто раньше начал позже закончит, а это крайне не желательно в текущем приложении -- чтение и обработка данных в реальном времени.
(Это больше теоритический вопрос, поскольку я через блокирующую очерь разведу получение и обработку данных, но все равно даже подготовка данных для
блокирующей очереди может быть под rc)
2)Второй вариант это async, что-то вроде:
while (true)
{
int result = await stream.ReadAsync(buffer, 0, 4096, cancellationToken);
if (result == 0)
{
break
}
HandleData(buffer);
}
Вопрос: как сейчас грамотнее делать APM или async? Вроде async более понятен для чтения и этот код эквивалентен 1-ому варианту APM.
Правильно ли я понимаю, что в обоих случаях будет вызываться iocp-поток? Соотв. если у меня будет много подобных соединений, то и iocp-потоков
должно быть много.
Заранее благодарю.
S>Вопрос: как сейчас грамотнее делать APM или async? Вроде async более понятен для чтения и этот код эквивалентен 1-ому варианту APM.
S>Правильно ли я понимаю, что в обоих случаях будет вызываться iocp-поток? Соотв. если у меня будет много подобных соединений, то и iocp-потоков
S>должно быть много.
Не вижу смысла делать случай 2 по двум причинам: 1) вызовешь ты ReadAsync раньше или позже вообще никак не повлияет на доступность данных — сетевая карта сложит данные в системный буфер когда они придут, и точный момент вызова тут не особо важен 2) rc там
теоретически возможен. То есть он может есть, может нет, а может он только на какой-нибудь из поддерживаемых систем появится — это всё будет зависеть от деталей реализации, и ловить будет очень сложно.
По async: внутри сокетов всё переписано на async, и все новые оптимизации продолжают делаться для async. Для .NET6 сам APM эмулируется через обёртки типа
TaskToApm, так что у тебя будут и накладные расходы на async и расходы на обёртку. Короче APM разумно оставить в прошлом.
В большинстве случаев сейчас самое правильное использовать или уже готовый паттерн
async + System.IO.Pipelines, или свой велосипед написанный по мотивам и ускоряющий какие-то специфичные для задачи сценарии.
Здравствуйте, hi_octane, Вы писали:
S>>Вопрос: как сейчас грамотнее делать APM или async? Вроде async более понятен для чтения и этот код эквивалентен 1-ому варианту APM.
S>>Правильно ли я понимаю, что в обоих случаях будет вызываться iocp-поток? Соотв. если у меня будет много подобных соединений, то и iocp-потоков
S>>должно быть много.
_>Не вижу смысла делать случай 2 по двум причинам: 1) вызовешь ты ReadAsync раньше или позже вообще никак не повлияет на доступность данных — сетевая карта сложит данные в системный буфер когда они придут, и точный момент вызова тут не особо важен 2) rc там теоретически возможен. То есть он может есть, может нет, а может он только на какой-нибудь из поддерживаемых систем появится — это всё будет зависеть от деталей реализации, и ловить будет очень сложно.
Я тут не совсем понимаю как работает сеть, но данных в буффере у меня может быть сильно больше чем 4096, соотв. после ReadAsync() во 2-м случае 2-й поток стартует практически
параллельно с 1м. Ну т.е. пока я обрабатываю (вычитал) 4096 байт из буффера, там отправитель отправит еще данные, которые будут сложены в буффер. Т.е. как
я вычитал 4096 байт, тут же там появлоись еще. Или было 8192 байт.
_>По async: внутри сокетов всё переписано на async, и все новые оптимизации продолжают делаться для async. Для .NET6 сам APM эмулируется через обёртки типа TaskToApm, так что у тебя будут и накладные расходы на async и расходы на обёртку. Короче APM разумно оставить в прошлом.
_>В большинстве случаев сейчас самое правильное использовать или уже готовый паттерн async + System.IO.Pipelines, или свой велосипед написанный по мотивам и ускоряющий какие-то специфичные для задачи сценарии.
Проект под 4.7.1