Асинхронные методы и асинхронные делегаты

С# 3.0 в двух словах говорит, что асинхронные методы и асинхронные делегаты похожи, но поведение очень отличается.

Вот что говорит книга обо всех.

Асинхронные методы

  • Редко или никогда не блокирует нить.
  • Метод Begin не может сразу вернуться к вызывающему.
  • Согласованный протокол без поддержки языка С#.

Асинхронные делегаты

  • Может блокироваться на любое время
  • BeginInvoke немедленно возвращается к вызывающему.
  • Встроенная поддержка компилятора.

В книге также говорится: Цель асинхронных методов - разрешить многим задачам работать на нескольких потоках; целью асинхронных делегатов является выполнение задачи параллельно с вызывающим.

Когда я просмотрел метод BeginRead() в классе System.IO.Stream через отражатель, он использует делегат и вызывает на нем BeginInvoke. Таким образом, асинхронный метод использует внутренний асинхронный делегат.

  • В таком случае, как можно сказать, что их поведение отличается? Поскольку он использует делегаты внутри страны, возможно ли сравнение, подобное приведенному выше?
  • Считаете ли вы, что работа с методом BeginXXX для делегата - это способ выполнить функцию параллельно с вызывающим?
  • Каков правильный способ реализации асинхронных методов, поддерживая все преимущества, такие как хорошее использование CPU?

Любые мысли?

Ответ 1

В основе, есть два основных поведения, которые вы можете увидеть, когда вы вызываете BeginFoo() с обратным вызовом.

  • Работа начинается в фоновом потоке, и этот поток будет использоваться все время до завершения работы и обратного вызова (например, потому что работа является синхронной).
  • Хотя какая-то работа происходит в фоновом потоке, поток не должен использоваться все время (например, потому что в работе используется System IO, которая может планировать обратные вызовы, например, IOCompletionPort).

При использовании делегата происходит поведение # 1.

Некоторые API (имеющие базовую поддержку для неблокирующих вызовов ввода-вывода) поддерживают поведение # 2.

В конкретном случае "Поток" я не уверен, но я предполагаю, что это абстрактный базовый класс, и поэтому это всего лишь поведение по умолчанию для подкласса, который реализует только синхронную версию Read. Подкласс "хороший" переопределит BeginRead/EndRead, чтобы иметь неблокирующую реализацию.

Преимущество # 2, как вы сказали, состоит в том, что вы можете иметь, например, 100 ожидающих вызовов ввода-вывода без потребления 100 потоков (потоки дороги).

Ответ 2

  • Реализация может быть разной; например, вызов асинхронного ввода-вывода может использовать использование портов завершения, чтобы минимизировать затраты на систему, не делая ничего.
  • Это, безусловно, способ; вы также можете использовать BackgroundWorker, ThreadPool.QueueUserWorkItem или Parallel.For (и т.д.) в .NET 4.0
  • Зависит от реализации

Я думаю, что книга пытается подчеркнуть, что делегаты всегда включают этот шаблон:

  • синхронный вызов (Invoke), который может блокировать
  • асинхронный вызов (BeginInvoke), который не должен блокироваться, если поток-пул не насыщен

но это не единственный шаблон. Также; (например, методы асинхронного ввода-вывода в Silverlight или в WebClient): вместо IAsyncResult для завершения сигнала используется событие.