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

Асинхронные системы с резьбой

Мне любопытно, что async ожидает встроенные функции потоковой передачи.

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

Но рассмотрим этот сценарий.

У меня есть async methodA, выполняющий операцию async в базе данных. Api базы данных предоставляет функцию BeginQuery и событие QueryCompleted. Я завернул их в задачу (с использованием TaskCompletionSource).

Мой вопрос заключается в том, что происходит под капотом между вызовом BeginQuery и срабатыванием QueryCompleted.

Я имею в виду - не нужно ли создавать какого-то рабочего, чтобы запустить это событие? На очень низком уровне должен быть некоторый синхронный цикл, который блокирует результат чтения нити с db.

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

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

При вызове асинхронного метода всегда создается новый рабочий поток?

Может ли кто-нибудь подтвердить мое понимание?

4b9b3361

Ответ 1

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

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

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

Нет. На уровне ОС все операции ввода/вывода являются асинхронными. Это синхронные API-интерфейсы, которые блокируют поток, в то время как основной асинхронный ввод-вывод выполняется. Я недавно написал это в сообщении в блоге: нет нити.

Ответ 2

Я имею в виду - не нужно ли создавать какого-то рабочего, чтобы мероприятие? На очень низком уровне должен быть некоторый синхронный цикл, который блокировка результата чтения нити из db.

Он создаст порт завершения ввода-вывода (IOCP), представляющий задачу, которая обрабатывается снаружи, и поток будет продолжаться с другими вещами. Затем, когда IOCP уведомляет о том, что задача выполнена, поток будет зависеть от состояния IOCP и продолжит выполнение задачи.

http://www.drdobbs.com/cpp/multithreaded-asynchronous-io-io-comple/201202921

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

Ответ 3

Я имею в виду - не нужно ли создавать какого-то рабочего, чтобы мероприятие? На очень низком уровне должен быть некоторый синхронный цикл, который блокировка результата чтения нити из db.

Даже когда вам действительно нужно ждать объекта ядра (например, ручное событие reset), вы все равно можете превратить блокирующий синхронный код в асинхронный и освободить поток от блокировки (обновлено: реальный сценарий).

Например, синхронный код:

void Consume()
{
    var completedMre = new ManualResetEvent(false);

    producer.StartOperation(completedMre);

    completedMre.WaitOne(); // blocking wait

    Console.WriteLine(producer.DequeueResult());
}

Асинхронный аналог:

async Task ConsumeAsync()
{
    var completedMre = new ManualResetEvent(false);

    producer.StartOperation(completedMre);

    var tcs = new TaskCompletionSource<Result>();

    ThreadPool.RegisterWaitForSingleObject(completedMre, 
        (s, t) => tcs.SetResult(producer.DequeueResult()),
        null, Timeout.Infinite, true);

    var result = await tcs.Task;
    Console.WriteLine(result);
}

Асинхронная версия масштабируется более чем в 64 раза (MAXIMUM_WAIT_OBJECTS), что максимальное количество объектов ядра, которое может быть агрегировано RegisterWaitForSingleObject для ожидания в одном потоке). Таким образом, вы можете вызвать Consume() в 64 раза параллельно, и он будет блокировать 64 потока. Или вы можете вызвать ConsumeAsync в 64 раза и заблокировать только один поток.

Ответ 4

Асинхронные методы не создают новые потоки, они основаны на Task s, которые могут или не могут использовать потоки (как и в случае с TaskCompletionSource), и даже если они не делают никаких накладных расходов, поскольку они используют ThreadPool.

Ответ 5

Здесь превосходное сообщение в блоге о задачах, потоках, асинхронном ожидании и параллельном программировании: https://blogs.msdn.microsoft.com/benwilli/2015/09/10/tasks-are-still-not-threads-and-async -is-не-параллельный/

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