Подтвердить что ты не робот

Как отличается асинхронное программирование от Threads?

Я читал несколько статей async здесь: http://www.asp.net/web-forms/tutorials/aspnet-45/using-asynchronous-methods-in-aspnet-45, и автор говорит:

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

Так что я пытаюсь понять, как он становится async, если мы не используем нити для параллельного выполнения? Что значит "вы не всегда используете поток".?

Позвольте мне сначала объяснить, что я знаю относительно работы с потоками (быстрый пример, конечно, темы могут использоваться в разных ситуациях, отличных от методологии UI и Worker)

  • У вас есть поток пользовательского интерфейса для ввода ввода, вывода.
  • Вы можете обрабатывать вещи в потоке пользовательского интерфейса, но это делает пользовательский интерфейс невосприимчивым.
  • Итак, давайте скажем, что у нас есть операция, связанная с потоком, и нам нужно загрузить какие-то данные.
  • И мы также разрешаем пользователям делать другие вещи во время их загрузки.
  • Мы создаем новый рабочий поток, который загружает файл и изменяет индикатор выполнения.
  • Как только это будет сделано, нечего делать, чтобы поток был убит.
  • Мы продолжаем из потока пользовательского интерфейса.

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

Не то же самое для программирования async? Если нет, какая разница? Я читал, что для программирования async используется ThreadPool для переноса потоков.

4b9b3361

Ответ 1

Для асинхронного программирования потоки не нужны.

"Асинхронный" означает, что API не блокирует вызывающий поток. Это не означает, что есть другой поток, который блокирует.

Сначала рассмотрим пример вашего пользовательского интерфейса, на этот раз с использованием реальных асинхронных API:

  • У вас есть поток пользовательского интерфейса для ввода ввода, вывода.
  • Вы можете обрабатывать вещи в потоке пользовательского интерфейса, но это делает пользовательский интерфейс невосприимчивым.
  • Итак, давайте скажем, что у нас есть операция, связанная с потоком, и нам нужно загрузить какие-то данные.
  • И мы также разрешаем пользователям делать другие вещи во время их загрузки.
  • Мы используем асинхронные API-интерфейсы для загрузки файла. Ни один рабочий поток не нужен.
  • Асинхронная операция сообщает о своем прогрессе в потоке пользовательского интерфейса (который обновляет индикатор выполнения), а также сообщает о его завершении потоку пользовательского интерфейса (который может реагировать на него, как и любое другое событие).

Это показывает, как может быть задействован только один поток (поток пользовательского интерфейса), но также выполняются асинхронные операции. Вы можете запускать несколько асинхронных операций и, тем не менее, иметь только один поток, задействованный в этих операциях, - ни один из них не блокирует их.

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

ASP.NET аналогичен, за исключением того, что у него нет основного/пользовательского потока. Вместо этого у него есть "контекст запроса" для каждого неполного запроса. Потоки ASP.NET поступают из пула потоков, и они вводят "контекст запроса", когда они работают над запросом; когда они закончены, они выходят из "контекста запроса" и возвращаются в пул потоков.

ASP.NET отслеживает неполные асинхронные операции для каждого запроса, поэтому, когда поток возвращается в пул потоков, он проверяет, выполняются ли какие-либо асинхронные операции для этого запроса; если их нет, то запрос завершен.

Итак, когда вы await выполняете неполную асинхронную операцию в ASP.NET, поток будет увеличивать этот счетчик и возвращать. ASP.NET знает, что запрос не является полным, потому что счетчик не равен нулю, поэтому он не завершает ответ. Поток возвращается в пул потоков, и в этот момент: на этот запрос нет потоков.

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

ASP.NET vNext немного отличается; там больше поддержки асинхронных обработчиков по всей структуре. Но общее понятие одно и то же.

Для получения дополнительной информации:

Ответ 2

В первый раз, когда я увидел асинхронный и ожидание, я был ими как синтаксический сахар С# для модели асинхронного программирования. Я был не прав, async и ждут больше. Это новый асинхронный шаблон Асинхронный шаблон на основе задач, http://www.microsoft.com/en-us/download/details.aspx?id=19957 - хорошая статья для начала. Большинство классов FCL, которые реализуют TAP, вызывают методы APM (BegingXXX() и EndXXX()). Вот два кода для TAP и AMP:

Пример TAP:

    static void Main(string[] args)
    {
        GetResponse();
        Console.ReadLine();
    }

    private static async Task<WebResponse> GetResponse()
    {
        var webRequest = WebRequest.Create("http://www.google.com");
        Task<WebResponse> response = webRequest.GetResponseAsync();
        Console.WriteLine(new StreamReader(response.Result.GetResponseStream()).ReadToEnd());
        return response.Result;
    }

Пример APM:

    static void Main(string[] args)
    {
        var webRequest = WebRequest.Create("http://www.google.com");
        webRequest.BeginGetResponse(EndResponse, webRequest);
        Console.ReadLine();
    }

    static void EndResponse(IAsyncResult result)
    {
        var webRequest = (WebRequest) result.AsyncState;
        var response = webRequest.EndGetResponse(result);
        Console.WriteLine(new StreamReader(response.GetResponseStream()).ReadToEnd());
    }

Наконец, эти два будут такими же, потому что GetResponseAsync() вызывает BeginGetResponse() и EndGetResponse() внутри. Когда мы отражаем исходный код GetResponseAsync(), мы получим код следующим образом:

task = Task<WebResponse>.Factory.FromAsync(
       new Func<AsyncCallback, object, IAsyncResult>(this.BeginGetResponse), 
       new Func<IAsyncResult, WebResponse>(this.EndGetResponse), null);

Для APM в BeginXXX() существует аргумент для метода обратного вызова, который будет вызываться, когда задача (обычно это тяжелая операция ввода-вывода) завершена. Создавая новый поток и асинхронный, оба из них сразу возвращаются в основной поток, оба они разблокируются. С точки зрения производительности создание нового потока будет стоить больше ресурсов при обработке операций ввода-вывода, таких как чтение файла, работа с базой данных и чтение в сети. Существует два недостатка в создании нового потока,

  • как в вашей упомянутой статье, есть стоимость памяти, а CLR - ограничение на пул потоков.
  • Контекстный коммутатор произойдет. С другой стороны, асинхронный не создавать поток вручную, и он не будет иметь контекстного переключателя когда возвращаются операции с привязкой к IO.

Вот изображение, которое поможет понять различия:

enter image description here

Эта диаграмма из статьи MSDN " Асинхронные страницы в ASP.NET 2.0", в которых очень подробно описывается, как старая асинхронная работа в ASP.NET 2.0.

О модели асинхронного программирования, пожалуйста, обратитесь к статье Джеффри Рихтера " Внедрение модели асинхронного программирования CLR", также есть более подробная информация в его книге "CLR через Csharp 3rd Edition" в главе 27.

Ответ 3

Позволяет себе представить, что вы реализуете веб-приложение и по мере того, как каждый запрос клиента приходит к ваш сервер, вам нужно сделать запрос на базу данных. Когда приходит запрос клиента, пул потоков поток вызывается в ваш код. Если вы сейчас выдаете запрос базы данных синхронно, поток блокируется на неопределенное время, ожидая, когда база данных ответит результатом. Если за это время другой клиентский запрос, пул потоков должен будет создать другой поток, и снова это поток будет блокироваться, когда он сделает другой запрос базы данных. Поскольку все больше и больше запросов клиентов поступают, все больше потоков создается, и все эти потоки блокируют ожидание ответа базы данных. В результате ваш веб-сервер выделяет множество системных ресурсов (потоки и их память), которые почти не используются! И чтобы усугубить ситуацию, когда база данных отвечает с различными результатами, потоки становятся разблокированы, и все они начинают выполняться. Но поскольку у вас может быть много потоков, и несколько процессорных ядер, Windows должна выполнять частые переключатели контекста, что ухудшает производительность даже Больше. Это не способ реализовать масштабируемое приложение.

Чтобы прочитать данные из файла, я теперь вызываю ReadAsync вместо Read. ReadAsync внутренне выделяет Объект задачи для представления ожидающего завершения операции чтения. Затем, ReadAsync вызывает функцию ReadFile Win32s (# 1). ReadFile выделяет свой IRP, инициализирует его так же, как и в синхронный сценарий (# 2), а затем передает его в ядро ​​Windows (# 3). Windows добавляет IRP для очереди IRP-драйверов жесткого диска (# 4), но теперь вместо того, чтобы блокировать поток, ваш поток разрешено вернуться к вашему коду; ваш поток немедленно возвращается из своего вызова в ReadAsync (# 5, # 6, и № 7). Теперь, конечно, IRP не обязательно обрабатывается, поэтому вы не можете получить код после ReadAsync, который пытается получить доступ к байтам в переданном байте [].