Асинхронные методы и асинхронные делегаты
С# 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
для завершения сигнала используется событие.