настраиваемый ожидаемый объект для SocketAsyncEventArgs.
Это выполнимо, но SocketAsyncEventArgs
специально предназначен для сценариев, чрезвычайно чувствительных к производительности. Подавляющему большинству (я имею в виду >99,9%) проектов он не нужен и вместо него можно использовать TAP. TAP удобен тем, что хорошо взаимодействует с другими методами... такими как отмена.
что мне нужно, так это ожидаемое отмену, и блог не охватывает эту тему. Также Стивен Клири, к сожалению, не описывает в своей книге, как отменить асинхронные методы, которые не поддерживают отмену.
Итак, здесь есть пара вещей. Начиная с «как отменить асинхронные методы, которые не поддерживают отмену»: краткий ответ: вы не можете. Это потому, что отмена является кооперативной; поэтому, если одна сторона не может сотрудничать, это просто невозможно. Более длинный ответ заключается в том, что это возможно, но обычно больше проблем, чем того стоит.
В общем случае «отменить неотменяемый метод» вы можете запустить метод синхронно в отдельном потоке, а затем прервать этот поток; это самый эффективный и самый опасный подход, поскольку прерванные потоки могут легко повредить состояние приложения. Чтобы избежать этой серьезной проблемы с повреждением данных, наиболее надежным подходом является запуск метода в отдельном процессе, который можно корректно завершить. Тем не менее, это обычно излишне и приносит больше проблем, чем оно того стоит.
Достаточно общего ответа; конкретно для сокетов вы не можете отменить одну операцию, потому что это оставляет сокет в неизвестном состоянии. Возьмем пример Connect
в вашем вопросе: между соединением и отменой есть состояние гонки. Ваш код может оказаться с подключенным сокетом после того, как запросит отмену, и вы не хотите, чтобы этот ресурс остался. Таким образом, нормальный и общепринятый способ отменить подключение к сокету — закрыть сокет.
То же самое касается любой другой операции с сокетом: если вам нужно прервать чтение или запись, то, когда ваш код выдает отмену, он не может знать, какая часть чтения/записи завершена, и это оставит сокет в неизвестном состоянии. состояние в отношении протокола, который вы используете. Правильный способ "отменить" любую операцию с сокетом — закрыть сокет.
Обратите внимание, что эта «отмена» находится в другом масштабе, чем обычно наблюдаемая отмена. CancellationToken
и друзья представляют собой отмену одной операции; закрытие сокета отменяет все операции для этого сокета. По этой причине API-интерфейсы сокетов не принимают CancellationToken
параметров.
Стивен Клири, к сожалению, не описывает в своей книге, как отменить асинхронные методы, которые не поддерживают отмену. Я пытался реализовать это сам
Вопрос "как отменить неотменяемый метод" почти никогда не решается должным образом с помощью "использовать этот блок кода". В частности, используемый вами подход отменяет ожидание, а не операцию. После запроса на отмену ваше приложение все еще пытается подключиться к серверу и в конечном итоге может завершиться успешно или неудачно (оба результата будут проигнорированы).
В моей библиотеке AsyncEx у меня есть пара из WaitAsync
, которые позволяют вам сделать что-то вроде этого:
private static async Task MyMethodAsync(CancellationToken cancellationToken)
{
var connectTask = _socket.ConnectAsync(_host);
await connectTask.WaitAsync(cancellationToken);
...
}
Я предпочитаю этот тип API, потому что ясно, что отменяется асинхронное ожидание, а не операция подключения.
Я терплю неудачу с TaskCompletionSource и Task.WhenAny, потому что с ожидаемым я на самом деле не ожидаю задачи.
Что ж, чтобы сделать что-то более сложное, вам нужно преобразовать ожидаемый объект в Task
. Возможно, есть более эффективный вариант, но это действительно лезет в сорняки. И даже если вы разберетесь со всеми деталями, вы все равно получите «фальшивую отмену»: API, который выглядит так, как будто отменяет операцию, но на самом деле это не так — он просто отменяет ожидание .
Это то, что я хотел бы иметь: возможность использовать Socket ConnectAsync с форматом TAP и возможность отменить его, сохраняя при этом возможность повторного использования SocketAsyncEventArgs.
TL;DR: вы должны закрыть сокет вместо отмены операции подключения. Это самый чистый подход с точки зрения быстрого восстановления ресурсов.
person
Stephen Cleary
schedule
06.12.2019