Вопрос

Я использую асинхронные методы в некоторых своих проектах, и мне это нравится, так как это позволяет моему приложению быть намного более масштабируемым.Однако мне интересно, как асинхронные методы действительно работают в фоновом режиме?Как .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 и т.д.).Как я мог бы отслеживать реальную продолжительность вызова с помощью асинхронного метода (с момента реального выполнения запроса до его завершения)?


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

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

Решение

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

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

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

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

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

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

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

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

Вот документы для Пул потоков класс и статья о асинхронные методы это лучше объясняет, что происходит.

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

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

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

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

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