Я пытаюсь выполнить некоторую работу с асинхронным вводом-выводом отдельно от потока пользовательского интерфейса. Где-то читал:

  • 1) Для кода, привязанного к ЦП, вы ждете операции, которая запускается в фоновом потоке с помощью метода Task.Run. Например, вычисление простых чисел
  • 2) Для кода, привязанного к вводу-выводу, вы ждете операции, которая возвращает задачу или задачу внутри асинхронного метода. Например, ожидание сети или базы данных

Итак, я сделал это:

// in windows form UI
private async void btnImport_Click(object sender, EventArgs e) {
    // [...]
    List<DataRow> rows = await importer.ImportDataAsync(123, 456);
    // [...]
}

// in Importer.ImportDataAsync:
public async Task<List<DataRow>> ImportDataAsync(int parent, int child, CancellationToken token = default(CancellationToken)) {

    // [...]
    List<DataRow> list = await RealImportFromDB(parent, child);
    return list;
    // [...]
}


public List<DataRow> RealImportFromDB(int p, int c) {

    List<DataRow> rowList;
    // here fetch the rows from DB over slow network
    // and return the row list
    return rowList;
}

При таком подходе пользовательский интерфейс заблокирован. Если я вызову RealImportFromDB (...) вот так

List<DataRow> l = await Task.Run(() => RealImportFromDB(parent, child));

Пользовательский интерфейс не заблокирован, но это противоречит пункту 2) сверху IMHO.

Где я делаю что-то не так?

С уважением, Алексей

2
alex999 15 Сен 2018 в 13:26

2 ответа

Лучший ответ

public List<DataRow> RealImportFromDB(int p, int c) - это блокирующий вызов базы данных, поэтому для его асинхронного выполнения вы использовали номер 1, в котором вы заключили вызов в Task.Run, что освободит поток Ui, как ожидалось.

При таком подходе пользовательский интерфейс заблокирован. Если я вызову RealImportFromDB (...)

Поскольку метод не предназначен для асинхронного вызова, он не возвращает Task или Task<T>, что является общим требованием для выполнения асинхронного вызова.

Ваш код await RealImportFromDB(parent, child) неверен, это ошибка компиляции, так как вы можете только ожидать вызовов, которые реализуют внутреннюю проверку GetAwaiter() (this и this), и наиболее распространенным сценарием является возврат Task или Task<T>, есть и другие типы

Попробуем разобраться в двух ваших утверждениях:

1) Для кода, привязанного к ЦП, вы ждете операции, которая запускается в фоновом потоке с помощью метода Task.Run. Например, вычисление простых чисел

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

2) Для кода, привязанного к вводу-выводу, вы ждете операции, которая возвращает задачу или задачу внутри асинхронного метода. Например, ожидание сети или базы данных

Чтобы реализовать это, вам нужен метод, который по умолчанию является Async и возвращает Task или Task<T>, такие методы являются частью всех структур данных, для каждого метода синхронизации в настоящее время существует соответствующий метод async для запуска асинхронное выполнение, и они являются настоящими вызовами ввода-вывода, где они не используют поток, поскольку обработка выполняется не в одном процессе, а на границе сети / процесса, поэтому вызывающий поток не должен ждать, ему просто нужно вернуться и выберите результат, когда он появится (любой поток пула потоков, не обязательно поток диспетчеризации). Внутри таких методов используется TaskCompletionSource<T> (Когда использовать TaskCompletionSource), который имеет механизм для уведомления вызывающего абонента о завершении сетевого вызова

1
Mrinal Kamboj 15 Сен 2018 в 10:56

Чтобы реализовать это, вам понадобится метод, который по умолчанию является Async и возвращает Task или Task.

Большое спасибо, это была моя проблема. Мой метод RealImportFromDB(...) не является асинхронным, поскольку он работает со старой проприетарной библиотекой, которая кажется не готовой для асинхронных вызовов.

Это были мои мысли: с ожиданием результата от ImportDataAsync(...) все, что вызывается внутри (например, RealImportFromDB(...)), отправляется из потока пользовательского интерфейса также . Так сказать: все внутри ImportDataAsync(...) инкапсулировано / запускается во втором, неблокирующем потоке.

@others: да, вы правы, образец из моего кода даже не компилируется. Много возился, поэтому пример кода не показывает все, что было изменено, извините за это: -}

0
alex999 15 Сен 2018 в 15:02