Как работают асинхронные методы в С#?

116
7

Я использую асинхронные методы в каком-то из моих проектов, и мне это нравится, поскольку это позволяет моему приложению быть намного более масштабируемым. Тем не менее, мне интересно, как асинхронные методы действительно работают в фоновом режиме? Как .NET(или Windows?) Знает, что вызов завершен? В зависимости от количества асинхронных вызовов, которые я сделал, я вижу, что новые потоки создаются (не всегда, хотя...). Почему?


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


for (int i = 0; i < 10000; i++)
{
myWebService.BeginMyMethod(new MyRequest(), result, new AsyncCallback(callback), i);
stopWatches[i].Start();
}
// Call back stop the stopwatch after calling EndMyMethod

Это не работает, поскольку все запросы (10000) имеют одинаковое начальное время, и продолжительность будет линейно возрастать (вызов 0 = продолжительность 1, вызов 1 = продолжительность 2 и т.д.). Как я могу контролировать реальную продолжительность вызова с помощью асинхронного метода (с момента выполнения запроса до конца)?


UPDATE: блокирует ли асинхронный метод поток? Я понимаю, что он использует .NET ThreadPool, но как IAsyncResult знает, что вызов завершен, и время для вызова метода CallBack?

спросил(а) 2009-10-24T03:12:00+04:00 11 лет, 1 месяц назад
1
Решение
117

Возможно, что большинство времени выполнения происходит до BeginMyMethod(). В этом случае ваши измерения будут слишком низкими. Фактически, в зависимости от API, BeginMyMethod() может вызывать обратный вызов перед выходом из самого стека. Перемещение вызова на StopWatch.Start() должно помочь.

ответил(а) 2009-10-24T04:46:00+04:00 11 лет, 1 месяц назад
83

Код - это железная дорога, а нить - поезд. Когда поезд идет по железной дороге, он выполняет код.


BeginMyMethod выполняется основным потоком. Если вы заглянете внутрь BeginMyMethod, он просто добавит делегат из MyMethod в очередь ThreadPool. Фактический MyMethod выполняется одним из поездов поезда. Процедура завершения, которая вызывается при выполнении MyMethod, выполняется тем же потоком, который выполнил MyMethod, а не вашим основным потоком, который запускает остальную часть кода. В то время как поток пула потоков занят выполнением MyMethod, основной поток может либо проехать какую-то другую часть железнодорожной системы (выполнить какой-то другой код), либо просто поспать, ожидая, пока не загорится определенный семафор.

Поэтому нет такой вещи, как IAsyncResult "знать", когда вызывать процедуру завершения, вместо этого процедура завершения - это просто делегат, вызванный потоком пула потоков сразу после выполнения выполнения MyMethod.


Надеюсь, вы не против какой-то детской аналогии поезда, я знаю, что это помогло мне не раз, когда объясняло многопоточность людям.

ответил(а) 2009-10-24T04:29:00+04:00 11 лет, 1 месяц назад
59

Асинхронные методы работают с использованием .NET ThreadPool. Они будут подталкивать работу к потоку ThreadPool (потенциально создавая один, если необходимо, но обычно просто повторно используя его), чтобы работать в фоновом режиме.


В вашем случае вы можете делать то, что делаете, однако понимаете, что ThreadPool имеет ограниченное количество потоков, с которыми он будет работать. Вы собираетесь создавать свою работу в фоновом потоке, а первая запускается сразу, но через некоторое время они будут стоять в очереди и не работать до тех пор, пока "задачи" не будут выполнены до конца. Это приведет к появлению потоков, занимающих больше времени и дольше.


Однако, ваши критерии секундомера несколько испорчены. Вы должны измерить общее время, необходимое для выполнения N задач, а не N раз для выполнения одной задачи. Это будет гораздо более полезной метрикой.

ответил(а) 2009-10-24T03:18:00+04:00 11 лет, 1 месяц назад
59

Суть его в том, что вызов Begin вызывает запрос на выполнение вашего метода. Фактически этот метод выполняется на ThreadPool, который представляет собой набор рабочих потоков, предоставляемых средой выполнения.


Threadpool - это фиксированный набор потоков для хруста в async-задачах, поскольку они попадают в очередь. Это объясняет, почему вы видите, что время выполнения занимает больше времени и дольше - ваши методы могут выполняться примерно в одно и то же время, но они не запускаются до тех пор, пока не будут выполнены все предыдущие методы в очереди.

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


Здесь docs для класса ThreadPool и статьи о асинхронные методы, которые лучше объясняют, что происходит.

ответил(а) 2009-10-24T03:17:00+04:00 11 лет, 1 месяц назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема