Вопрос

Обычно я с опаской отношусь к частичной реализации интерфейсов.Однако, IAsyncResult это немного особый случай, учитывая, что он поддерживает несколько совершенно разных шаблонов использования.Как часто вы используете / видите использованные AsyncState/AsyncCallback шаблон, в отличие от простого вызова EndInvoke, используя AsyncWaitHandle, или опрос IsCompleted (фу)?

Связанный с этим вопрос: Обнаружение того, что рабочий элемент ThreadPool завершен / ожидает завершения.

Рассмотрим этот класс (очень приблизительный, требуется блокировка):

public class Concurrent<T> {
    private ManualResetEvent _resetEvent;
    private T _result;

    public Concurrent(Func<T> f) {
        ThreadPool.QueueUserWorkItem(_ => {
                                         _result = f();
                                         IsCompleted = true;
                                         if (_resetEvent != null)
                                             _resetEvent.Set();
                                     });
    }

    public WaitHandle WaitHandle {
        get {
            if (_resetEvent == null)
                _resetEvent = new ManualResetEvent(IsCompleted);
            return _resetEvent;
        }

    public bool IsCompleted {get; private set;}
    ...

Это имеет WaitHandle (лениво созданный, точно так же, как описано в IAsyncResult документация) и IsCompleted, но я не вижу разумной реализации для AsyncState ({return null;}?).Итак, имеет ли смысл это реализовывать IAsyncResult?Обратите внимание , что Task в библиотеке параллельных расширений реализуется IAsyncResult, но только IsCompleted реализуется неявно.

Это было полезно?

Решение

  • По моему опыту, простой вызов EndInvoke без предварительного ожидания или перезвона редко бывает полезен
  • Простого предоставления обратных вызовов иногда недостаточно, так как ваши клиенты могут захотеть дождаться нескольких операций одновременно (WaitAny, WaitAll).
  • Я никогда не опрашивал IsCompleted, действительно, гадость!Итак, вы могли бы сохранить реализацию IsCompleted, но это настолько просто, что, похоже, не стоит потенциально удивлять ваших клиентов.

Таким образом, разумная реализация для асинхронно вызываемого метода действительно должна обеспечивать полностью реализованный IAsyncResult.

Кстати, вам часто не нужно самостоятельно реализовывать IAsyncResult, просто возвращайте то, что возвращается Delegate .BeginInvoke .Смотрите реализацию System.IO.Stream.BeginRead для примера.

Другие советы

Похоже, у вас есть пара вопросов.Давайте разберемся с ними по отдельности

Ленивое создание WaitHandle

Да, это самый правильный подход.Вы должны сделать это потокобезопасным способом, но это ленивый способ.

Хитрость, однако, заключается в том, чтобы избавиться от ручки ожидания.WaitHandle - это база IDisposable и должен своевременно утилизироваться.Документация для IAsycResult не распространяется на этот случай.Лучший способ сделать это - в EndInvoke.В документации для BeginInvoke явно указано, что для каждого BeginInvoke должен быть соответствующий EndInvoke (или BeginRead /EndRead).Это лучшее место, где можно избавиться от ручки для ожидания.

Как следует реализовать AsyncState?

Если вы посмотрите на стандартные BCL API, которые возвращают IAsyncResult, большинство из них принимают параметр состояния.Обычно это значение, которое возвращается из AsyncState (пример см. в API сокетов).Хорошей практикой является включение переменной состояния, типизированной как object, для любого API в стиле BeginInvoke API, который возвращает IAsyncResult.Не обязательно, но хорошая практика.

При отсутствии переменной состояния допустимо возвращать значение null.

IsCompleted API (завершенный API)

Это будет сильно зависеть от реализации, которая создает IAsyncResult.Но да, вы должны это реализовать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top