Реализация IAsyncResult явно - PullRequest
6 голосов
/ 02 января 2009

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

Смежный вопрос: Обнаружение того, что WorkItem 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 в библиотеке Parallel Extensions реализует IAsyncResult, но только IsCompleted реализован неявно.

Ответы [ 2 ]

3 голосов
/ 02 января 2009

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

Создание WaitHandle лениво

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

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

Как должен быть реализован AsyncState?

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

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

IsCompleted API

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

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

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

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

...